Как работают виртуальные деструкторы? - PullRequest
6 голосов
/ 27 апреля 2010

Несколько часов назад я возился с проблемой утечки памяти, и оказалось, что я действительно неправильно понял некоторые основные вещи о виртуальных деструкторах! Позвольте мне объяснить мой классный дизайн.

class Base
{
  virtual push_elements()
  {}
};

class Derived:public Base
{
vector<int> x;
public:
   void push_elements(){ 
      for(int i=0;i <5;i++)
         x.push_back(i); 
   }
};

void main()
{
    Base* b = new Derived();
    b->push_elements();
    delete b;
}

Инструмент проверки границ сообщил об утечке памяти в производном векторе классов. И я понял, что деструктор не является виртуальным, и деструктор производного класса не вызывается. И это удивительно исправилось, когда я сделал деструктор виртуальным. Разве вектор не освобождается автоматически, даже если деструктор производного класса не вызывается? Это странная вещь в инструменте BoundsChecker или мое понимание виртуального деструктора неверно?

Ответы [ 8 ]

15 голосов
/ 27 апреля 2010

Удаление объекта производного класса через указатель базового класса, когда базовый класс не имеет виртуального деструктора, приводит к неопределенному поведению.

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

8 голосов
/ 27 апреля 2010

Если базовый класс не имеет виртуального деструктора, то результатом вашего кода будет неопределенное поведение, не обязательно вызываемое неверным деструктором. Вероятно, это то, что диагностирует BoundsChecker.

2 голосов
/ 27 апреля 2010

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

Причина, по которой это происходит, та же самая причина, по которой «неправильная» функция вызывается при попытке переопределить не виртуальную функцию-член и вызывать ее через базовый указатель.

1 голос
/ 06 ноября 2010

если вы пришли из c #, то вы правы, задаваясь вопросом, почему вектор не выделяется автоматически. Но в c ++ автоматическое управление памятью невозможно, если вы не используете Microsoft Manged Extesions для C ++ (C ++ / CLI).

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

1 голос
/ 27 апреля 2010

В C ++ тривиальный деструктор является рекурсивно определенной концепцией - это деструктор, который компилятор написал для вас, когда у каждого члена класса (и каждого базового класса) есть тривиальный деструктор. (Существует похожая концепция, называемая тривиальным конструктором.)

Если в объект включен объект с нетривиальным деструктором (например, vector в вашем примере), то деструктор внешнего объекта (например, Derived) в нем больше не является тривиальным. Даже если вы не написали деструктор, компилятор C ++ автоматически написал деструктор, который вызывает деструкторы всех членов, у которых есть деструкторы.

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

1 голос
/ 27 апреля 2010

Из C ++ FAQ Lite: "Когда мой деструктор должен быть виртуальным?" Прочитайте это здесь . (Кстати, C ++ FAQ Lite - отличный источник всех ваших вопросов, связанных с C ++).

1 голос
/ 27 апреля 2010

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

0 голосов
/ 29 июня 2010

Деструктор - это функция-член класса, чье имя совпадает с именем класса и ему предшествует знак тильды (~). Деструктор используется для уничтожения объекта класса, когда объект выходит из области видимости или вы можете сказать, что вся очистка от уничтожения класса должна выполняться в деструкторе. Вся память, выделяемая при создании объекта в классе, разрушается (или освобождается память), когда объект выходит из области видимости.

Найти более подробную информацию с примером на BoundsCheck

...