2013-07-14 1 views
2

Я использую boost-variant, и при переключении типов в варианте я хочу, чтобы вызываемый деструктор вызывался. Следующий код «работает», но я не уверен, почему. Я чувствую, что он должен segfault, поскольку он вызывает удаление по неинициализированному указателю. Есть ли какая-то маска с ускорением-модификацией за кулисами?Вызов деструктора для сопоставленного типа в форсированном варианте

#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

Что заставляет вас думать, что _Boost.Variant_ не назовет соответствующих деструкторов? –

+0

В качестве примечания: вы можете создать шаблон '' operator ('t *)' '' '' '' '' '' '' '' '' '' '' ', потому что он всегда удаляется независимо от типа :-) – Gabriel

ответ

2

По Boost doc для boost::variant:

«Никогда-Empty» Гарантии

Все экземпляры v вариантной гарантии типа, что v построил содержание одного из типов Ti, даже если операция по v ранее не удалось.

Глядя в «повышающем/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 * получает нуль-init. Это также означает, что B * имеет нулевое значение, поскольку варианты можно рассматривать как объединение.

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>(); 
1

Вызов delete на неинициализированном указателе не определенно поведение, что означает ничего может произойти, не включая ничего.

Особенно, если неинициализированный указатель находится в памяти, которая ранее не использовалась, маловероятно, что эта память будет содержать нуль, поэтому удаление получает нулевой указатель и ничего не делает.

Второй результат (!) - это то, что вы получите segfault, как вы ожидали, потому что указатель находится в памяти, которая не содержит действительного значения указателя.

Другие возможности: указатель находится в месте, содержащем адрес полностью несвязанного объекта, уничтожая его (вероятно, при вызове совершенно неправильного деструктора). Или указатель указывает на кучу, но где-то посередине, и вы повредите внутреннюю структуру кучи, вызвав таинственные сбои намного позже.

Этот список ни в коем случае не является исчерпывающим.

Смежные вопросы