正在调用boost变量中匹配类型的析构函数

Calling the destructor for matched type in boost-variant

本文关键字:类型 析构函数 调用 boost 变量      更新时间:2023-10-16

我使用的是boost变量,在变量中切换类型时,我希望确保调用析构函数。以下代码"有效",但我不确定原因。我觉得它应该segfault,因为它在未初始化的指针上调用delete。幕后是否有某种助推变体的魔力?

#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

根据boost::variant:的Boost文档

"永不清空"保证

类型变体的所有实例v都保证v已经构造了类型Ti之一的内容,即使对v的操作先前已经失败。

查看"boost/variat.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
}

对于有界的变量类型,第一个类型将获得默认的init。这意味着对于您的req类型,A *的init为零。这也意味着B *是零初始化,因为变量可以被视为一个并集。

在未初始化的指针上调用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>();

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

特别是如果未初始化的指针正好在以前没有使用过的内存中,那么这个内存不太可能包含零,所以delete会得到一个空指针,什么也不做。

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

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

这份清单绝非详尽无遗。

相关文章: