Деструктор не вызывается при удалении указателя void

у меня 3 класса

class A
{
    A();
    virtual ~A();
}
class B : public A
{
    B();
    ~B();
}
class C
{
    void *obj;
    C() : obj(nullptr) {}
    ~C() { if (obj) delete obj; }
}

когда я использую класс C в качестве контейнера для любого дочернего элемента класса A и пытаюсь удалить экземпляр C. A, B деструктор не вызывается, это нормально? Каково решение?

C* instance = new C();
instance.obj = new B();
//Magic
delete instance; // A and B destructor is not called

person EOG    schedule 03.10.2013    source источник
comment
delete хорошо понимает типы. Если вы delete void *, он не будет знать, что объект изначально был C.   -  person Shahbaz    schedule 03.10.2013
comment
Если вы используете C в качестве контейнера для любого потомка класса A, почему вы не можете использовать A * для типа obj?   -  person CB Bailey    schedule 03.10.2013
comment
Вы не можете удалить пустой указатель. Это недопустимый C++, и он даже не должен компилироваться. Если это так, вы не используете настоящий компилятор C++, и этот вопрос не помечен соответствующим образом.   -  person Kerrek SB    schedule 03.10.2013
comment
@KerrekSB: удаление void* не является неправильным; просто неопределенное поведение, если оно не равно нулю. Мой (достаточно реальный) компилятор просто выдает предупреждение.   -  person Mike Seymour    schedule 03.10.2013
comment
@MikeSeymour: Да, так лучше. Но это одно из тех предупреждений, которые вы действительно не должны игнорировать. Возможно, это также немного унаследовано от стандарта. Зачем разрешать пустой указатель, если единственным допустимым значением является NULL? Могли бы и все это выкинуть. Вы по-прежнему можете разрешить delete nullptr.   -  person Kerrek SB    schedule 03.10.2013


Ответы (3)


Удаление указателя на несовместимый тип (включая void) приводит к неопределенному поведению.

Каково решение?

  • используйте правильный тип: либо тип, указанный с помощью new, либо базовый класс, если он имеет виртуальный деструктор; или
  • используйте std::shared_ptr<void>, инициализированный из std::shared_ptr<correct_type>: его средство удаления сделает правильную вещь.

В этом случае похоже, что вы можете просто хранить A*, а не void*, поскольку вы говорите, что это должен быть «контейнер для любого дочернего элемента класса A».

Кстати, нет необходимости проверять, является ли указатель нулевым, перед его удалением.

person Mike Seymour    schedule 03.10.2013
comment
+1 за shared_ptr<void>. Очень хорошо. (Возможно, также можно инициализировать из unique_ptr<T>.) - person Kerrek SB; 03.10.2013

Вы удаляете void*, поэтому delete не знает, что это B*, поэтому деструктор не может быть вызван. Вы должны использовать указатель класса, если хотите, чтобы деструктор вызывался при удалении.

Например, все классы, которые могут быть obj C, расширяют A, а затем используют A*.

person Geoffroy    schedule 03.10.2013

Да, для delete требуется определенный тип.

person kevinwei    schedule 03.10.2013