在boost-variant中调用析构函数以匹配类型

时间:2013-07-14 19:50:24

标签: c++ boost boost-variant

我正在使用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

3 个答案:

答案 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获取空指针并且什么都不做。

第二个可能的(!)结果是你得到了你预期的段错误,因为指针恰好位于不包含有效指针值的内存中。

其他可能性是:指针恰好位于包含完全不相关对象的地址的位置,从而破坏它(可能在调用完全错误的析构函数时)。或者指针指向堆,但是在中间某处,你会破坏内部堆结构,导致后来发生神秘崩溃。

该清单并非详尽无遗。