我正在使用boost-variant,当在变量中切换类型时,我想确保调用析构函数。以下代码“有效”,但我不确定原因。我觉得它应该是段错误,因为它在未初始化的指针上调用删除。幕后会有一些助推变异魔法吗?
#include <iostream>
#include <boost/variant.hpp>
using namespace std;
class A
{
public:
A() {}
virtual ~A() { cout << "Destructing A" << endl; }
};
class B
{
public:
B() {}
virtual ~B() { cout << "Destructing B" << endl; }
};
typedef boost::variant<A*, B*> req;
class delete_visitor : public boost::static_visitor<void>
{
public:
inline void operator() (A *a) const
{
cout << "Will destruct A" << endl;
delete a;
}
inline void operator() (B *b) const
{
cout << "Will destruct B" << endl;
delete b;
}
};
class Wrapper
{
public:
Wrapper(int s) {
setBackend(s);
}
virtual ~Wrapper() {
// cleanup
boost::apply_visitor(delete_visitor(), my_pick);
}
void setBackend(int s)
{
// make sure if we already have put something in our variant, we clean it up
boost::apply_visitor(delete_visitor(), my_pick);
if(s == 0)
my_pick = new A();
else
my_pick = new B();
}
private:
req my_pick;
};
int main()
{
Wrapper *w = new Wrapper(0);
w->setBackend(1);
delete w;
return 0;
}
以下是我输出的内容:
Will destruct A
Will destruct A
Destructing A
Will destruct B
Destructing B
答案 0 :(得分:2)
根据boost::variant
的{{1}}:
“永不空”保证
类型变体的所有实例v都保证v构造了其中一种类型Ti的内容,即使v上的操作先前已失败。
查看“boost / variant.hpp”,特别是变体的默认构造函数,您会看到:
// boost/variant.hpp: 1383
variant()
{
// NOTE TO USER :
// Compile error from here indicates that the first bound
// type is not default-constructible, and so variant cannot
// support its own default-construction.
//
new( storage_.address() ) internal_T0();
indicate_which(0); // zero is the index of the first bounded type
}
对于有界的变体类型,第一种类型获取default-init。这意味着对于req
类型,A *
获取零init。这也意味着B *
是零初始化,因为变体可以被视为联合。
答案 1 :(得分:1)
对未初始化的指针调用delete
是Undefined Behavior。它编译的事实并不能使代码合法化。但无论如何,我认为你应该使用内存管理来做这件事:
typedef boost::variant<boost::shared_ptr<A>, boost::shared_ptr<B>> req;
// ....
if (s == 0)
my_pick = boost::make_shared<A>();
else
my_pick = boost::make_shared<B>();
答案 2 :(得分:1)
在未初始化的指针上调用delete
是未定义的行为,这意味着任何都可能发生,包括什么都没有。
特别是如果未初始化的指针恰好位于以前没有使用的内存中,则此内存不会包含零,因此delete获取空指针并且什么都不做。
第二个可能的(!)结果是你得到了你预期的段错误,因为指针恰好位于不包含有效指针值的内存中。
其他可能性是:指针恰好位于包含完全不相关对象的地址的位置,从而破坏它(可能在调用完全错误的析构函数时)。或者指针指向堆,但是在中间某处,你会破坏内部堆结构,导致后来发生神秘崩溃。
该清单并非详尽无遗。