Деструктор базового класса вызывается без уничтожения базового класса! - PullRequest
1 голос
/ 26 октября 2010
#include<iostream>
using namespace std;

class A
{
public:
        int i;
        A() {cout<<"A()"<<endl;}
        ~A() {cout<<"~A()"<<endl;}
};
class B:public A
{
public:
        int j;
        B(): j(10)
        {
                this->i=20;
                this->~A();
        }
};

int main()
{
        B abc;
        cout<<"i="<<abc.i<<" j="<<abc.j<<endl;
}//main

Два вопроса:

  1. Почему деструктор A вызывается как обычная функция вместо разрушения объекта?(или это какое-то правило, что базовый класс будет уничтожен, только если деструктор дочернего класса вызовет деструктор базового класса?) Я пробовал этот пример кода, чтобы выяснить, как работает деструктор.Поэтому, если простой вызов функции-деструктора не разрушает объект, то, очевидно, существует какой-то другой вид вызова, который вызывает деструктор, и только тогда объект разрушается.Что такого особенного в вызове такого типа и что это за вызов?
  2. Есть ли способ получить список инициализации для A в конструкторе B?Примерно так:

    class B:public A
    { 
        B(): j(10), A():i(20) {}
    };
    

Ответы [ 7 ]

5 голосов
/ 26 октября 2010
  1. Деструктор подобен любой другой нормальной функции, которую вы можете вызвать (но вы никогда не должны делать это, если не используете новое размещение). Когда вы вызываете delete для объекта, происходят две вещи: деструктор вызывается для очистки, а затем operator delete вызывается для освобождения памяти, выделенной для объекта. Здесь второго шага не происходит.

  2. Нет, это нельзя так назвать. Что вы можете сделать, это что-то вроде этого:

    класс А { общественности: A (int n): i (n) {} };

    класс B: общедоступный A { общественности: B (): A (20), j (10) {} };

5 голосов
/ 26 октября 2010
  1. Деструктор базового класса должен быть виртуальным.Здесь, поскольку он создан в стеке, это не проблема, но в любом случае ..
  2. Нет, но вы можете вызвать конструктор class A() в списке инициализации конструктора B, например:
    B(): A( .. ), ...

A* a = new B();
//..
delete a;

будет не вызывать деструктор B, если только class A деструктор не является виртуальным .Вот почему контейнеры STL не должны быть получены - их деструкторы не являются виртуальными.

3 голосов
/ 26 октября 2010

@ Nav: нет, ваше понимание «уничтожено» просто неверно. Когда вызывается деструктор объекта, объект уничтожается. Кажется, вы верите, что память, в которой он хранился, полностью испаряется, но этого никогда не происходит. Объект больше не существует, но некоторые ненужные данные обычно остаются объектом, и если вы хотите нарушить правила C ++ и вызвать неопределенное поведение, вы можете прочитать эти оставшиеся байты, и они будут выглядят как объект, и поскольку во время выполнения не проверяется, обращаетесь ли вы к действительному объекту, вы часто рассматриваете их как объект. Что вы делаете.

Это незаконно, это неопределенное поведение, но на практике это часто работает.

Еще раз, деструктор физически не испаряет память. Ваша оперативная память по-прежнему имеет такую ​​же емкость после выполнения деструктора. Концептуально объект больше не существует после запуска деструктора. Но содержащиеся в нем данные все еще находятся в памяти.

3 голосов
/ 26 октября 2010

Для точки:

  1. Это неопределенное поведение, но вызывается только ~ A (), хотя экземпляр класса B, потому что ~ A () не объявлен виртуальным. Подробнее на Википедии .
  2. Нет. Для производных классов сначала вызовите родительский класс, затем назначьте параметры.

Для пункта 1) в Википедии:

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

Пример (для пункта 2):

B(): A(), j(10) {}

или

B(): A() {j = 10;}
2 голосов
/ 26 октября 2010

Если вы вызовете ~ SomeClass () самостоятельно, будет вызвана функция деструктора.Что оставляет объект (в данном случае, часть базового класса объекта) в разрушенном состоянии.

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

Попытка выяснить, действительно ли A уничтожена простым использованием члена i, не является хорошим тестом.На самом деле, вы не можете проверить это, поскольку использование объекта приводит к неопределенному поведению.Это может сработать, а может и нет (в вашем случае, вероятно, будет напечатано «i = 20 j = 10», но я уже уничтожен).

2 голосов
/ 26 октября 2010

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

Если i был чем-то более сложным, чем int (например, vector)попытка сделать что-нибудь с этим, вероятно, приведет к сбою.

2 голосов
/ 26 октября 2010

1) Порядок вызова деструктора в C ++ - это обратный порядок порядка вызова конструктора.Итак, сначала получаемый объект класса get destroy, а затем объект базового класса.

2) No.

...