2

私はブースト バリアントを使用しています。バリアントの型を切り替えるときに、デストラクタが呼び出されるようにしたいと考えています。次のコードは「機能します」が、その理由はわかりません。初期化されていないポインターで削除を呼び出しているため、セグメンテーション違反が発生するはずです。舞台裏でブーストバリアントの魔法が行われていますか?

#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

Boost docによるとboost::variant

「空にならない」保証

型バリアントのすべてのインスタンス v は、v に対する操作が以前に失敗した場合でも、v が型 Ti のいずれかのコンテンツを構築したことを保証します。

「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 *は、ゼロ初期化を取得します。B *これは、バリアントを共用体と見なすことができるため、初期化がゼロであることも意味します。

于 2013-07-14T21:45:22.530 に答える
1

初期化されていないポインターの呼び出しdeleteは未定義の動作です。

特に、初期化されていないポインタがたまたま以前に使用されたことのないメモリにある場合、このメモリにゼロが含まれる可能性は低くないため、delete は null ポインタを取得して何もしません。

2 番目に可能性の高い (!) 結果は、期待どおりにセグメンテーション違反が発生することです。これは、有効なポインター値が含まれていないメモリにポインターがたまたまあるためです。

他の可能性は次のとおりです: ポインターは、まったく無関係なオブジェクトのアドレスを含む場所にあり、それを破壊します (完全に間違ったデストラクタを呼び出している可能性があります)。または、ポインターがヒープを指しているが、途中のどこかを指している場合、内部ヒープ構造が破損し、後で不可解なクラッシュが発生します。

そのリストは決して網羅的なものではありません。

于 2013-07-14T20:14:24.607 に答える