std :: remove_if多态性std :: std :: vector的simel_ptr
std::remove_if polymorphic std::unique_ptr from std::vector
i具有三个类的层次结构,其中 Derived
来自 Selectable
和 Drawable
。然后,我有一个std::unique_ptr<Drawable>
的std::vector
,我用Derived
对象填充。
i是某些,矢量只能由同时从两个碱基中得出的对象填充。
我尝试通过使用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());
}
显然,如果我将selected
成为Drawable
的指针,一切都很好,但这不是我的意图。
我遇到了一个运行时错误,这会导致程序崩溃。为什么会发生这种情况?我将如何解决?
关键问题在于 std::remove_if
"删除"元素的方式:
删除是通过移动(通过移动分配(完成的 范围内的元素以某种方式不适合 被删除出现在范围的开头。相对顺序 保留的元素被保留,物理大小 容器没有变化。迭代器指向 新的逻辑端和范围的物理端仍然 可定性,,但元素本身具有未指定的值 (根据可移动后的条件条件(。
基本上,您可以保留auto ptr = vec[2].get()
获取的原始指针,但没有人保证ptr
仍然有效。您只能确保vec[2]
有效。(在过滤之前曾经在vec[2]
中的独特指针位于新的逻辑端和物理端之间,以未指定的值为单位(。
在您的示例中,当std::remove_if
到达第三个元素时,谓词返回true
和remove_if
调用vec[2].get()
的Destructor。由于您将原始指针保留在该指针上,因此您使用的是已经被破坏的对象的指针。
您程序崩溃的原因是您将dynamic_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());
这是可能的输出:
$ ./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
根本不是由您的编译器生成的,因为不需要。因此,在这种情况下,您的程序避免了崩溃。
在Valgrind下运行时,我看到的第一个错误是
Invalid read of size 8
at 0x4CCE92D: __dynamic_cast (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.22)
by 0x109139: _ZZ4mainENKUlOT_E_clIRSt10unique_ptrI8DrawableSt14default_deleteIS4_EEEEDaS0_ (43706186.cpp:27)
by 0x10917B: _ZN9__gnu_cxx5__ops10_Iter_predIZ4mainEUlOT_E_EclINS_17__normal_iteratorIPSt10unique_ptrI8DrawableSt14default_deleteIS9_EESt6vectorISC_SaISC_EEEEEEbS2_ (predefined_ops.h:241)
by 0x10902D: _ZSt11__remove_ifIN9__gnu_cxx17__normal_iteratorIPSt10unique_ptrI8DrawableSt14default_deleteIS3_EESt6vectorIS6_SaIS6_EEEENS0_5__ops10_Iter_predIZ4mainEUlOT_E_EEESE_SE_SE_T0_ (stl_algo.h:866)
by 0x108F78: _ZSt9remove_ifIN9__gnu_cxx17__normal_iteratorIPSt10unique_ptrI8DrawableSt14default_deleteIS3_EESt6vectorIS6_SaIS6_EEEEZ4mainEUlOT_E_ESC_SC_SC_T0_ (stl_algo.h:937)
by 0x108EBC: main (43706186.cpp:25)
Address 0x5892dc0 is 0 bytes inside a block of size 16 free'd
at 0x4A0A2DB: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x109BFC: Derived::~Derived() (43706186.cpp:15)
by 0x109D21: std::default_delete<Drawable>::operator()(Drawable*) const (unique_ptr.h:76)
by 0x10A7C4: std::unique_ptr<Drawable, std::default_delete<Drawable> >::reset(Drawable*) (unique_ptr.h:347)
by 0x10A39D: std::unique_ptr<Drawable, std::default_delete<Drawable> >::operator=(std::unique_ptr<Drawable, std::default_delete<Drawable> >&&) (unique_ptr.h:254)
by 0x109062: _ZSt11__remove_ifIN9__gnu_cxx17__normal_iteratorIPSt10unique_ptrI8DrawableSt14default_deleteIS3_EESt6vectorIS6_SaIS6_EEEENS0_5__ops10_Iter_predIZ4mainEUlOT_E_EEESE_SE_SE_T0_ (stl_algo.h:868)
by 0x108F78: _ZSt9remove_ifIN9__gnu_cxx17__normal_iteratorIPSt10unique_ptrI8DrawableSt14default_deleteIS3_EESt6vectorIS6_SaIS6_EEEEZ4mainEUlOT_E_ESC_SC_SC_T0_ (stl_algo.h:937)
by 0x108EBC: main (43706186.cpp:25)
Block was alloc'd at
at 0x4A0921F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x10942E: std::_MakeUniq<Derived>::__single_object std::make_unique<Derived>() (unique_ptr.h:791)
by 0x108DE2: main (43706186.cpp:21)
从中,我们可以看到添加到数组中的一个元素已被删除,但是我们仍然尝试通过捕获的指针(selected
(在lambda中尝试dynamic_cast
。
如果我们将铸件移动在擦除呼叫的呼叫之外,则在删除元素之前,只执行dynamic_cast
一次:
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());
此版本已完成,没有Valgrind的警告。
通过,请注意,您可以编写lambda接受const auto&
,因为您不打算修改元素。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- 如何从 std::atomic 中提取指针 T<T>?
- 为什么 std::unique 不调用 std::sort?
- 使用std::函数映射对象方法
- 可组合的lambda/std::函数与std::可选