C++ 如果多态std::unique_ptr来自std::vector,则删除
我有一个由三个类组成的层次结构,其中C++ 如果多态std::unique_ptr来自std::vector,则删除,c++,polymorphism,c++14,C++,Polymorphism,C++14,我有一个由三个类组成的层次结构,其中Derived派生自selective和Drawable。然后我有一个std::vector的std::unique_ptr,我用派生的对象填充它 我确信向量将只由同时从两个基派生的对象填充 当我试图通过使用指向Selected的指针从向量中删除某个元素时,就会出现问题 #include <vector> #include <memory> #include <algorithm> struct Selectable {
Derived
派生自selective
和Drawable
。然后我有一个std::vector
的std::unique_ptr
,我用派生的对象填充它
我确信向量将只由同时从两个基派生的对象填充
当我试图通过使用指向Selected
的指针从向量中删除某个元素时,就会出现问题
#include <vector>
#include <memory>
#include <algorithm>
struct Selectable {
virtual ~Selectable() = 0;
};
Selectable::~Selectable() = default;
struct Drawable {
virtual ~Drawable() = 0;
};
Drawable::~Drawable() = default;
struct Derived : Selectable, Drawable {};
int main()
{
std::vector<std::unique_ptr<Drawable>> vec;
for (int i = 0; i < 5; ++i) {
vec.push_back(std::make_unique<Derived>());
}
Selectable* selected = dynamic_cast<Selectable*>(vec[2].get());
vec.erase(std::remove_if(vec.begin(), vec.end(),
[selected](auto&& ptr) {
return ptr.get() == dynamic_cast<Drawable*>(selected);
}), vec.end());
}
#包括
#包括
#包括
结构可选{
虚拟~可选()=0;
};
可选::~可选()=默认值;
结构可拉伸{
virtual~Drawable()=0;
};
Drawable::~Drawable()=默认值;
结构派生:可选,可绘制{};
int main()
{
std::vec;
对于(int i=0;i<5;++i){
向量推回(std::make_unique());
}
可选*selected=dynamic_cast(vec[2].get());
向量擦除(std::remove_if(vec.begin(),vec.end(),
[所选](自动和ptr){
返回ptr.get();
}),vec.end());
}
显然,如果我将选中
作为指向可绘制
的指针,一切都很好,但这不是我的意图
我收到一个运行时错误,导致程序崩溃。为什么会发生这种情况,我该如何解决 关键问题在于std::remove\u如果删除元素:
移除是通过移动(通过移动分配)来完成的
范围中的元素,使
将被删除的内容显示在范围的开头。相对顺序
保留下来的元素和
容器不变。指向元素之间的元素的迭代器
新的逻辑端和范围的物理端仍然是
可取消引用,但元素本身具有未指定的值
(根据可移动和可分配的立柱条件)
因此,基本上,您保留了由auto ptr=vec[2].get()获取的原始指针,但没有人保证ptr
保持有效。仅保证vec[2]
有效。(在筛选之前,唯一指针以前位于vec[2]
中,现在位于新的逻辑端和物理端之间,未指定值)
在您的示例中,当std::remove_if
到达第三个元素时,谓词返回true
和remove_if
调用vec[2]。get()
的析构函数。
由于保留了指向它的原始指针,因此使用的是指向已销毁对象的指针。程序崩溃的原因是调用dynamic\u cast
查找无效指针。只需将输出添加到析构函数并打印所选内容即可轻松演示:
struct Selectable {
virtual ~Selectable();
};
Selectable::~Selectable() {
std::cout << "Selectable::~Selectable:" << this << std::endl;
};
struct Drawable {
virtual ~Drawable();
};
Drawable::~Drawable() {
std::cout << "Drawable::~Drawable:" << this << std::endl;
}
vec.erase(std::remove_if(vec.begin(), vec.end(),
[selected](auto&& ptr) {
std::cout << "selected:" << selected << std::endl;
return ptr.get() == dynamic_cast<Drawable*>(selected);
}), vec.end());
在无效指针上调用动态\u cast
显然,如果我将选中
作为指向可绘制
的指针,一切都很好,但这不是我的意图
在这种情况下,您也有一个无效的指针,但是dynamic\u cast
不是由编译器生成的,因为它不是必需的。因此,在这种情况下,您的程序避免了崩溃。在Valgrind下运行时,我看到的第一个错误是
大小为8的读取无效
在0x4CCE92D:uuu动态_cast(in/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.22)
根据0x109139:_ZZ4mainENKUlOT_E_clIRSt10unique_ptri8drawablest14 default_deleteIS4_eeedas0(43706186.cpp:27)
根据0x10917B:_ZN9______gnu_cx5___ops10_Iter_prediz4; mainulot_E_EclINS_17_正常_正常_迭代器10唯一_ptri8可绘图14默认_删除9_eest6矢量SaISC_eeeeee bs2_(预定义_ops.h:241)
0x10902D:_ZSt11_________gnu_cxx17___正常____迭代器10唯一_ptri8; 8可绘制14默认_删除3_eest6矢量6_seeens0_5__ops10_Iter_E_E_E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
通过0x108F78:_zst9删除\u ifIN9\u gnu\u cx17\u正常\u迭代器s10唯一\u ptri8可绘制的st14默认值\u删除3 ees6矢量6_sai6_eeez4mainulot\u ESC\u SC\u T0(stl\u算法:937)
通过0x108EBC:main(43706186.cpp:25)
地址0x5892dc0是大小为16 free'd的块中的0字节
在0x4A0A2DB处:运算符删除(void*)(在/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so中)
通过0x109BFC:Derived::~Derived()(43706186.cpp:15)
通过0x109D21:std::default_delete::operator()(可绘制*)常量(唯一的\u ptr.h:76)
通过0x10A7C4:std::unique_ptr::reset(可绘图*)(unique_ptr.h:347)
通过0x10A39D:std::unique_ptr::operator=(std::unique_ptr&&)(unique_ptr.h:254)
0x109062:_ZSt11_________gnu_cxx17___正常____迭代器10唯一_ptri8可绘图14默认_删除3_eest6矢量6_seeens 0_5__ops10_Iter_E_E_E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E
通过0x108F78:_zst9删除\u ifIN9\u gnu\u cx17\u正常\u迭代器s10唯一\u ptri8可绘制的st14默认值\u删除3 ees6矢量6_sai6_eeez4mainulot\u ESC\u SC\u T0(stl\u算法:937)
通过0x108EBC:main(43706186.cpp:25)
街区被分配在
在0x4A0921F处:新操作员(无符号长)(in/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
通过0x10942E:std::_MakeUniq::u单个_对象std::make_unique()(unique_ptr.h:791)
通过0x108DE2:main(43706186.cpp:21)
由此,我们可以看到添加到数组中的一个元素已被删除,但我们仍然尝试通过捕获的指针(选定的
)在lambda中执行动态\u转换
如果我们将强制转换移到erase remove调用之外,则在删除元素之前,仅执行一次动态\u强制转换
:
auto const s2 = dynamic_cast<Drawable*>(selected);
vec.erase(std::remove_if(vec.begin(), vec.end(),
[s2](auto&& ptr) {
return ptr.get() == s2;
}), vec.end());
auto const s2=动态_cast(已选择);
向量擦除(std::remove_if(vec.begin(),vec.end(),
[s2](自动和ptr){
返回ptr.get()==s2;
}),vec.end());
此版本运行至完成,Valgrind未发出任何警告
顺便说一下,
auto const s2 = dynamic_cast<Drawable*>(selected);
vec.erase(std::remove_if(vec.begin(), vec.end(),
[s2](auto&& ptr) {
return ptr.get() == s2;
}), vec.end());