作者:oFoUro_877 | 来源:互联网 | 2023-02-05 11:22
我有三个类的层次结构,Derived
派生自Selectable
和Drawable
.然后,我有一个std::vector
的std::unique_ptr
,我充满了Derived
对象.
我确信矢量将仅由同时来自两个碱基的物体填充.
当我尝试通过使用指针从向量中删除某个元素时,问题就出现了Selected
.
#include
#include
#include
struct Selectable {
virtual ~Selectable() = 0;
};
Selectable::~Selectable() = default;
struct Drawable {
virtual ~Drawable() = 0;
};
Drawable::~Drawable() = default;
struct Derived : Selectable, Drawable {};
int main()
{
std::vector> vec;
for (int i = 0; i <5; ++i) {
vec.push_back(std::make_unique());
}
Selectable* selected = dynamic_cast(vec[2].get());
vec.erase(std::remove_if(vec.begin(), vec.end(),
[selected](auto&& ptr) {
return ptr.get() == dynamic_cast(selected);
}), vec.end());
}
显然,如果我selected
成为一个指针Drawable
,一切都很好,但这不是我的意图.
我收到一个运行时错误,导致程序崩溃.为什么会发生这种情况,我该如何解决?
1> David Haim..:
关键问题在于std::remove_if
"移除"元素的方式:
通过移动(通过移动分配)范围中的元素来完成移除,使得不被移除的元素出现在范围的开头.保留的元素的相对顺序被保留,容器的物理大小不变.指向新逻辑端和范围的物理端之间的元素的迭代器仍然是可解除引用的,但元素本身具有未指定的值
(根据MoveAssignable后置条件).
所以基本上,你保留一个原始指针auto ptr = vec[2].get()
,但没有一个保证ptr
仍然有效.您只能保证vec[2]
有效.(vec[2]
过滤之前的唯一指针现在位于新逻辑端和物理端之间,未指定值).
在您的示例中,当std::remove_if
到达第三个元素时,谓词返回true
并remove_if
调用vec[2].get()
析构函数.因为你保持一个原始指针,你正在使用指向已被销毁的对象的指针.
@DeiDei使用`std :: find` +`std :: vector :: erase`
2> Sergei Kuren..:
程序崩溃的原因是你调用dynamic_cast
了无效指针.只需向析构函数添加输出并选择打印即可轻松演示:
struct Selectable {
virtual ~Selectable();
};
Selectable::~Selectable() {
std::cout <<"Selectable::~Selectable:" <(selected);
}), vec.end());
这是一个可能的输出:
$ ./a.exe
selected:0x3e3ff8
selected:0x3e3ff8
selected:0x3e3ff8
selected:0x3e3ff8
Drawable::~Drawable:0x3e3ffc
Selectable::~Selectable:0x3e3ff8
selected:0x3e3ff8
Segmentation fault
调用dynamic_cast
无效指针是未定义的行为.
显然,如果我selected
成为一个指针Drawable
,一切都很好,但这不是我的意图.
在这种情况下,您也有一个无效的指针,但dynamic_cast
由于它不是必需的,因此不会由编译器生成.在这种情况下,您的程序可以避免崩溃.