Возможна ли утечка памяти без виртуального деструктора?

#include <iostream>
using namespace std;
class base
{
   int a;
 public: 
   base() {a =0;}
 };
 class derv :public base
 {
   int b;
  public:
   derv() {b =1;}
 };
 int main()
 {
    base *pb = new derv();
    delete pb;
 }

У меня нет виртуального деструктора в классе derv, удаляет ли он только базовую часть объекта derv??


person Alok    schedule 06.01.2012    source источник
comment
Это базовый класс, которому нужен виртуальный деструктор.   -  person Yuushi    schedule 06.01.2012
comment
@Mysticial: у Джеймса есть этот.   -  person Puppy    schedule 06.01.2012
comment
@James, вы сказали, что даже в базовом классе нет виртуальной функции, но у него должен быть виртуальный деструктор, если мы хотим наследовать базовый класс ??   -  person Alok    schedule 06.01.2012


Ответы (3)


Это может быть.

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

Была запрошена цитата. С++ 11 §5.3.5/3 утверждает, что для скалярного выражения delete (т. е. не выражения delete[]):

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

Статический тип (base) отличается от динамического типа (derv), а у статического типа нет виртуального деструктора, поэтому поведение не определено.

person James McNellis    schedule 06.01.2012
comment
Отсутствие виртуального деструктора в базе означает, что любая пользовательская финализация, указанная в деструкторе производных классов, не будет выполняться. Память для объекта по-прежнему будет правильно освобождена. (Деструктор base также вызывался бы, если бы он был определен как не виртуальный). - person Xion; 06.01.2012
comment
@Xion: Действительно. Поведение не определено. Все ставки сделаны. Ничего нельзя сказать с уверенностью о поведении программы. - person James McNellis; 06.01.2012
comment
@wilhelmtell: это стандарт. Это неопределенное поведение. - person Puppy; 06.01.2012
comment
@wilhelmtell: Если вы настаиваете. :-) - person James McNellis; 06.01.2012
comment
base::~base() вызовут, derv::~derv() нет. После этого память будет освобождена. - person lapk; 06.01.2012
comment
Интригующий. Еще один случай, когда на практике снисходительные составители заметают UB под ковер. Хорошо знать! - person Xion; 06.01.2012
comment
@JamesMcNellis спасибо, это снимает все сомнения и должно отменить все отрицательные голоса! - person wilhelmtell; 06.01.2012
comment
@James, вы сказали, что даже в базовом классе нет виртуальной функции, но у него должен быть виртуальный деструктор, если мы хотим наследовать базовый класс ?? - person Alok; 06.01.2012
comment
@Xion: На самом деле, это не так уж и скрыто. Например. если базовый подобъект не находится по тому же адресу, что и производный объект, он вызовет функцию освобождения с неправильным адресом. Кроме того, деструкторы любых виртуальных баз могут быть вызваны неправильно (с неправильным this), хотя я не уверен в этом. - person jpalecek; 06.01.2012
comment
@Alok: Если вы delete динамически размещаете объект derv, используя base*, указывающий на него, то base должен иметь виртуальный деструктор. - person James McNellis; 06.01.2012
comment
@Alok: Нет, но тогда вы должны delete объекты классов только через указатели на большинство производных подобъектов. - person jpalecek; 06.01.2012
comment
@jpalecek Есть ли единственный компилятор, который фактически помещает base членов после derv членов? Структура памяти действительно не определена Стандартом, но на практике base всегда ставится первой. Здесь мы говорим о невиртуальном наследовании. - person lapk; 06.01.2012
comment
@AzzA: я не могу ответить на ваш вопрос, так как я не знаком с каждым компилятором. (Я уверен, что я даже не смог бы назвать их всех!) - person James McNellis; 06.01.2012
comment
@wilhelmtell: я согласен. Иногда полезно цитировать. Тем не менее, они также могут сделать ответ запутанным или чрезмерно техническим и педантичным, поэтому я стараюсь не делать этого, если только его не просят (что нормально) или этого требует субъект, например. если необходимо указать на какой-то нюанс или если есть разногласия относительно правильного поведения. - person James McNellis; 06.01.2012
comment
@JamesMcNellis Я согласен с вашим ответом в принципе - опубликованный простой код, скорее всего, никогда не пойдет не так, но крайне неразумно полагаться на реализации компилятора. Даже если они кажутся одинаковыми для всех компиляторов. Мне было просто любопытно, знает ли кто-нибудь о компиляторе, который делает необычное размещение памяти. - person lapk; 06.01.2012
comment
@Xion: Что ты имеешь в виду под ковер? Обратите внимание, что в стандарте явно указано, что компилятор не должен сообщать, когда он компилирует неопределенное поведение. - person GManNickG; 06.01.2012
comment
Эта проблема возникает снова и снова на StackOverflow. Есть ли способ исправить это в С++ 2x? Должен ли С++ разделять типы указателей на два типа? Вместо T* должны быть deletable_ptr<T> и notdeletable_ptr<T>. (Я знаю, что современный подход может использовать что-то вроде shared_ptr<T>, но я ищу «тонкий» подход, который ближе всего к базовому указателю, но при этом обеспечивает правильность). new должен возвращать deletable_ptr, а любая попытка преобразования в базовый класс должна преобразовываться в undeletable_ptr, если, конечно, у базы нет виртуального конструктора. - person Aaron McDaid; 06.01.2012
comment
@AaronMcDaid: Конечно, ничто не мешает вам создавать свои собственные утилиты, но в какой-то момент либо язык сильно изменится, либо использование ваших утилит станет настолько распространенным, что вы больше не программируете на C++. То, о чем вы просите, - это настоящая статическая и сильная система типов, которая в значительной степени является безнадежной в С++. - person GManNickG; 06.01.2012

В вашем исходном коде нет утечки памяти, так как у вас нет какой-либо переменной-члена, которая создается динамически.

Рассмотрим модифицированный пример ниже Случай 1:

#include <iostream>
using namespace std;
class base
{
   int a;
 public: 
   base() {a =0;}
   ~base() 
     {
       cout<<"\nBase Destructor called";

     }
 };
 class derv :public base
 {
   int *b;

  public:
   derv() { b = new int;}
  ~derv()
  {
      cout<<"\nDerv Destructor called"; 
      delete b;
  }
 };
 int main()
 {
    base *pb = new derv();
    delete pb;
 }

В этом случае вывод будет,

   Base Destructor called

В этом случае происходит утечка памяти, потому что «b» создается динамически с использованием «нового», который следует удалить с помощью ключевого слова «удалить». Поскольку деструктор derv не вызывается, он не удаляется, поэтому возникает утечка памяти.

Рассмотрим следующий случай 2:

#include <iostream>
using namespace std;
class base
{
   int a;
 public: 
   base() {a =0;}
   virtual ~base() 
     {
       cout<<"\nBase Destructor called";

     }
 };
 class derv :public base
 {
   int *b;

  public:
   derv() { b = new int;}
  ~derv()
  {
      cout<<"\nDerv Destructor called"; 
      delete b;
  }
 };
 int main()
 {
    base *pb = new derv();
    delete pb;
 }

В случае 2 выход будет,

Derv Destructor called 
Base Destructor called

В этом случае утечки памяти нет, потому что вызывается деструктор derv, а b удаляется.

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

Мы можем сказать: «Деструктор должен быть виртуальным, если производный класс имеет динамически созданные члены».

person Community    schedule 06.01.2012
comment
Поведение не определено. Все ставки сделаны. Ничего нельзя сказать с уверенностью о поведении программы. - person James McNellis; 06.01.2012
comment
@JamesMcNellis: в приведенном выше коде с выделением памяти в производном классе должен быть виртуальный деструктор. В противном случае происходит утечка памяти. - person Rajesh Subramanian; 09.01.2012

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

person Viral    schedule 06.01.2012
comment
Поведение не определено. Все ставки сделаны. Ничего нельзя сказать с уверенностью о поведении программы. - person James McNellis; 06.01.2012