2

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

3 回答 3

2

根据Boostboost::variant文档:

“永不空”保证

所有变体类型的实例 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
}

对于有界的变体类型,第一个类型获取默认初始化。这意味着对于您的req类型,A *获取零初始化。这也意味着B *零初始化,因为可以将变体视为联合。

于 2013-07-14T21:45:22.530 回答
1

调用delete未初始化的指针是未定义的行为。它编译的事实并不能使代码合法。不过,无论如何,我认为您应该对这种事情使用内存管理:

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>();
于 2013-07-14T20:09:22.220 回答
1

调用delete未初始化的指针是未定义的行为,这意味着任何事情都可能发生,包括什么都不会发生。

特别是如果未初始化的指针恰好位于以前未使用过的内存中,则该内存包含零的可能性不大,因此 delete 得到一个空指针并且什么都不做。

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

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

该清单绝不是详尽无遗的。

于 2013-07-14T20:14:24.607 回答