2010-10-26 2 views
1
#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) {} 
    }; 
    
+2

Можете ли вы представить код без Undefined Behavior, который проявляет какую бы то ни было очевидную проблему? Например, код без явного вызова деструктора? –

+1

-1 не записывайте глупый код (т. Е. Уничтожаете свой собственный базовый класс в конструкторе) – Jan

+1

Что на Земле есть 'this-> ~ A();' for? Неужели вы [должны понимать] (http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), что они делают, прежде чем использовать их? :) – GManNickG

ответ

3

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

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

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

+1

+1 для понимания проблемы ОП со всей концепцией «разрушения». –

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

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

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

+0

Нет. Наличие конструктора базового класса как виртуального не разрушает A. – Nav

+0

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

+2

Нет, деструктор базового класса не обязательно должен быть виртуальным. Но сложно сказать, что такое исходный код и проблема. Представленный код имеет только Undefined Behavior. –

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

  2. Нет, вы не можете назвать это так. Что вы можете сделать некоторые вещи, как это:

    класс А { общественности: A (Int N): я (п) {} };

    класса В: общественного { общественности: В(): А (20), J (10) {}} ;

+0

Ситуация, когда приемлемо называть деструктор, когда вы сами управляете памятью базового объекта и создаете объект, используя новое место размещения. Конечно, это очень редко. –

+0

Согласовано, обновлено соответствующим образом. – Naveen

2

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

2) Нет.

3

Для точки:

  1. Это неопределенное поведение, но только ~ А() вызывается, хотя экземпляром класса В, потому что ~ А() не объявлена ​​виртуальной. См. Больше на Wikipedia.
  2. №. Для производных классов сначала вызовите родительский класс, а затем назначьте параметры.

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

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

Пример (для точки 2):

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

или

B(): A() {j = 10;} 
2

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

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

2

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

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

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

Смежные вопросы