[英]Problems with shared_ptr and operator new
I get a bug in a test code, where I have to test a class located at the bottom of a diamond heritage, and above all, which forms a circular dependency with an other class, too difficult to change (I promise it is not my代码...)。
class A 的构造函数要求对 class B 的引用,并且 class B 要求对 ZA2F2ED4F8EBC2C6BBD4 A...
出于单元测试的目的,我想通过为 A 分配 memory 来打破这种依赖关系,但不构建实例。 然后通过将此 shared_ptr 传递给 B,一旦 B 被构建,通过构建 A 实例,其构造函数将引用 B。
我成功地在您可以在本地或任何在线编译器执行的示例代码中重现了该问题:
#include <iostream>
#include <memory>
using namespace std;
struct ILevel_0
{
virtual ~ILevel_0() = 0;
};
ILevel_0::~ILevel_0() {}
struct Level_1_A : virtual ILevel_0
{
int a;
};
struct Level_1_B : virtual ILevel_0
{
int b;
};
struct Level_2 : Level_1_A, Level_1_B
{
int c;
};
struct OtherStruct
{
OtherStruct(const std::shared_ptr<ILevel_0>& lev) : _lev(lev) {}
std::shared_ptr<ILevel_0> _lev;
};
int main()
{
std::shared_ptr<Level_2> level2;
void* level2_rawMemory = operator new(sizeof(Level_2));
Level_2* level2Ptr = static_cast<Level_2*>(level2_rawMemory);
level2.reset(level2Ptr);
std::cout << "------ 1" << std::endl;
std::shared_ptr<ILevel_0> level0 = level2; // OK
std::cout << "------ 2" << std::endl;
// OtherStruct otherStruct(level2); // KO : crash after ------ 1
std::cout << "------ 3" << std::endl;
// Needed, else the shared_ptr's deleter would crash by calling the delete instruction
new (level2.get()) Level_2{};
std::cout << "------ 4" << std::endl;
return 0;
}
可以看到一行(构建OtherStruct
实例)被注释掉了,这样代码代码就可以编译运行了,不会崩溃。 如果取消注释,程序将崩溃,不打印“----- 2”。 我不明白为什么,我也不明白为什么前面的指令(level0 building)没有崩溃。
感谢您的帮助,并为近似英语感到抱歉。
在 ASAN/UBSAN 下运行您的代码:
走过去就发现
39 std::shared_ptr<ILevel_0> level0 = level2; // OK
是罪魁祸首。
然而,真正的罪犯是这样的:
void* level2_rawMemory = operator new(sizeof(Level_2));
auto* level2Ptr = static_cast<Level_2*>(level2_rawMemory);
level2.reset(level2Ptr);
它违反了 C++ memory model。 您不能将原始 memory 重新解释为 Level_2 object。 特别是当数据类型不是 POD 时(它们不是,因为它们是虚拟的)。
而是使用
Level_2* level2Ptr = new Level_2;
level2.reset(level2Ptr);
或者确实
level2.reset(new Level_2);
最好的:
std::shared_ptr<Level_2> level2 = std::make_shared<Level_2>();
实际上考虑安全指针转换(请参阅使用 boost::function 并带有指向派生 class 的共享指针的参数):
std::shared_ptr<ILevel_0> level0 = dynamic_pointer_cast<ILevel_0>(level2); // OK
#include <iostream>
#include <memory>
#include <utility>
struct ILevel_0 { virtual ~ILevel_0() = default; };
struct Level_1_A : virtual ILevel_0 { int a{}; };
struct Level_1_B : virtual ILevel_0 { int b{}; };
struct Level_2 : Level_1_A, Level_1_B { int c{}; };
struct OtherStruct {
explicit OtherStruct(std::shared_ptr<ILevel_0> lev) : _lev(std::move(lev)) {}
std::shared_ptr<ILevel_0> _lev;
};
int main() {
std::cout << std::unitbuf;
std::shared_ptr<Level_2> level2 = std::make_shared<Level_2>();
std::cout << "------ 1" << std::endl;
auto level0 = std::dynamic_pointer_cast<ILevel_0>(level2); // OK
std::cout << "------ 2" << std::endl;
OtherStruct otherStruct(level2);
std::cout << "------ 3" << std::endl;
level2 = std::make_shared<Level_2>();
std::cout << "------ 4" << std::endl;
}
印刷
------ 1
------ 2
------ 3
------ 4
并且没有消毒剂警告。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.