Наследование и деструкторы - теоретический вопрос - C ++ - PullRequest
1 голос
/ 07 сентября 2011
class A
{
    public:
         virtual void f(){ printf("A.f "); }
         ~A(){ f(); }
};

class B : public A
{
    A a;

    public:
         void f(){ printf("B.f "); }
         B(){ throw -1; }
        ~B(){ f(); }
};

int main()
{
    try{ B b; }
    catch(...){ printf("Exc");}
}

Так вот как я это вижу. Внутри блока try ничего не печатается при построении B b;. Блок заканчивается. Я думаю, что компилятор сначала уничтожает член A a;. Так что A.f() будет напечатано. Означает ли это, что уничтожение экземпляра class B завершено? После этого компилятор просто вызовет ~A() (разрушающий базовый класс)?

Я думал, что должен был получить A.f(), затем B.f() (уничтожить экземпляр класса B) и после этого A.f() снова (деструктор базового класса). Компиляция этого заставила меня немного подумать. Exc печатается в конце, конечно. Я прошел несколько тем и ничего не нашел.

РЕДАКТИРОВАТЬ : Выход из Dev-C ++ (GCC 3.4.2) равен

A.f A.f Exc

Ответы [ 5 ]

10 голосов
/ 07 сентября 2011

У вас действительно есть два A объекта.

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

Когда вы создаете B b, вы создаете базовый класс A, а также экземпляр A a.

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

  • ~A() вызывается для экземпляра A a.
  • ~A() вызывается для базового класса A.

Это объясняетпочему вы получаете A.f A.f Exc.

* Деструктор 1037 * не будет вызван, потому что B не был полностью построен, так как его конструктор не завершился успешно.

4 голосов
/ 07 сентября 2011

Заказ должен быть: A.f, A.f, Exc

Когда вызывается конструктор B, перед входом первый конструктор A вызывается из-за наследования. Далее, перед вводом конструктора B (то есть до {), a создается по умолчанию.

Строительство Б будет завершено, только если оно достигнет соответствия }. Но до этого у вас есть бросок. Таким образом, частично построенный B должен быть уничтожен, у которого есть один объект a и унаследованный подобъект A. Таким образом, оба они уничтожены, следовательно, A.f и A.f

Затем вы достигаете броска, где печатается «Exc»

4 голосов
/ 07 сентября 2011

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

Однако для записи вывод вашего кода будет :

A.f A.f Exc


Почему?

  • Создание b завершается неудачей.
  • b * B не вызывается, но деструкторы для его членов являются 1 .
  • У него есть член типа A, деструктор которого вызывает функцию f().
  • Существует также полностью построенная A база b; поэтому деструктор b A также вызывается, вызывая A::f(), как и раньше.
  • Exc, конечно, выводится окружающим обработчиком исключений.

Это то, что вы хотели знать?


1

[n3290: 15.2/2]: Объект любой продолжительности хранения, чей инициализация или уничтожение прекращается, исключение будет иметь деструкторы выполняются для всех своих полностью построенных подобъектов (исключая членов-членов класса, подобного объединению), то есть для подобъекты, для которых главный конструктор (12.6.2) завершил казнь и деструктор еще не начал казнь. [..] * 1 050 *

0 голосов
/ 07 сентября 2011

Вы не можете вызывать виртуальные функции из конструктора или деструктора.Они не будут работать как виртуальные, но будут называться не виртуальными функциями.Вы можете прочитать об этом в FAQ здесь и в соответствующей теме о конструкторах здесь .

0 голосов
/ 07 сентября 2011
#include <stdio.h>

class A
{
    public:
         virtual void f(int i){ printf("A.f %i\n", i); }
         ~A(){ f(0); }
};

class B : public A
{
    A a;

    public:
         void f(int i){ printf("B.f %i\n", i); }
         B(){ throw -1; }
         ~B(){ f(1); }
};

int main()
{
    try{ B b; }
    catch(...){ printf("Exc\n");}
}

Разрушитель А вызывается дважды, вот и все.

Выход:

A.f 0
A.f 0
Exc
...