Почему я должен объявить виртуальный деструктор для абстрактного класса в C++?

158
задан j0k 1 July 2013 в 14:11
поделиться

5 ответов

Это еще более важно для интерфейса. Любой пользователь Вашего класса будет, вероятно, держать указатель на интерфейс, не указатель на конкретную реализацию. Когда они прибывают для удаления его, если деструктор будет невиртуальным, они назовут деструктор интерфейса (или обеспеченное компилятором значение по умолчанию, если Вы не определили один), не деструктор производного класса. Мгновенная утечка памяти.

, Например

class Interface
{
   virtual void doSomething() = 0;
};

class Derived : public Interface
{
   Derived();
   ~Derived() 
   {
      // Do some important cleanup...
   }
};

void myFunc(void)
{
   Interface* p = new Derived();
   // The behaviour of the next line is undefined. It probably 
   // calls Interface::~Interface, not Derived::~Derived
   delete p; 
}
186
ответ дан Daniel Kamil Kozar 23 November 2019 в 21:41
поделиться

Ответ на Ваш вопрос часто, но не всегда. Если Ваш абстрактный класс запрещает клиентам звонить, удаляют на указателе на него (или если это говорит так в его документации), Вы свободны не объявить виртуальный деструктор.

можно запретить клиентам звонить, удаляют на указателе на него путем создания его деструктора защищенным. Работая как это, это совершенно безопасно и разумно опустить виртуальный деструктор.

Вы в конечном счете закончите без таблицы виртуальных методов и закончите тем, что предупредили о своих клиентах Ваше намерение при создании его non-deleteable через указатель на него, таким образом, у Вас будет действительно причина не объявить его виртуальный в тех случаях.

[Посмотрите объект 4 в этой статье: http://www.gotw.ca/publications/mill18.htm ]

37
ответ дан kevinarpe 23 November 2019 в 21:41
поделиться

Да это всегда важно. Производные классы могут выделить память или содержать ссылку на другие ресурсы, которые должны будут быть очищены, когда объект будет уничтожен. Если Вы не даете Вашим интерфейсам/абстрактным классам виртуальные деструкторы, то каждый раз Вы удаляете экземпляр производного класса через дескриптор базового класса, которым не назовут деструктор Вашего производного класса.

Следовательно, Вы открываете потенциал для утечек памяти

class IFoo
{
  public:
    virtual void DoFoo() = 0;
};

class Bar : public IFoo
{
  char* dooby = NULL;
  public:
    virtual void DoFoo() { dooby = new char[10]; }
    void ~Bar() { delete [] dooby; }
};

IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted
8
ответ дан OJ. 23 November 2019 в 21:41
поделиться

Это не всегда требуется, но я нахожу, что он хорошая практика. То, что это делает, является им, позволяет производному объекту быть безопасно удаленным через указатель базового типа.

Так, например:

Base *p = new Derived;
// use p as you see fit
delete p;

плохо формируется, если Base не имеет виртуального деструктора, потому что он попытается удалить объект, как будто это было Base *.

7
ответ дан Evan Teran 23 November 2019 в 21:41
поделиться

Это не только хорошая практика. Это - правило № 1 для любой иерархии классов.

  1. основа большая часть класса иерархии в C++ должна иметь виртуальный деструктор

Теперь для Почему. Возьмите типичную иерархию животных. Виртуальные деструкторы проходят виртуальную отправку так же, как любой другой вызов метода. Возьмите следующий пример.

Animal* pAnimal = GetAnimal();
delete pAnimal;

Предполагают, что Животное является абстрактным классом. Единственный способ, которым C++ знает, что надлежащий деструктор звонит, через виртуальную отправку метода. Если деструктор не будет виртуальным тогда, то он просто назовет деструктор Животного и не уничтожит любые объекты в производных классах.

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

5
ответ дан JaredPar 23 November 2019 в 21:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: