Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如果多态std::unique_ptr来自std::vector,则删除_C++_Polymorphism_C++14 - Fatal编程技术网

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());