Я не знаю последовательность запуска кода, пожалуйста, научите меня Когда я создаю точку A *p=new C, что происходит? Я даже не могу понять это уравнение, почему их классы разные и его все еще можно скомпилировать?
#include <iostream>
using namespace std;
class A
{
public:
A( ){cout << "A Constructor" << endl;}
virtual ~A( ){cout << "A Destructor" << endl;}
virtual void f( ){cout << "A::f( )" << endl;}
void g( ){ f( ); }
};
class B:public A
{
public:
B( ){f();cout <<"B Constructor" << endl;}
~B( ){cout << "B Destructor" << endl;}
};
class C:public B
{
public:
C( ){f( ); cout << "C Constructor" << endl;}
~C( ){cout << "C Destructor" << endl;}
void f( ){cout << "C::f( )" << endl;}
};
int main()
{
A *p=new C;
p->g( );
delete p;
}
выход
A Constructor
A::f( )
B Constructor
C::f( )
C Constructor
C::f( )
C Destructor
B Destructor
A Destructor
Откуда вы взяли этот код? Какие ресурсы вы использовали, чтобы попытаться понять код? Что вы думаете об этом?
A *p=new C;
создает объект класса C
, создание этого объекта включает следующие шаги.
Вызывается конструктор класса A
, потому что если C получен из некоторых баз, то сначала вызываются конструкторы всех баз.
В конструкторе A выводится строка A Constructor
, следовательно, первая строка вывода.
Затем вызывается конструктор B, потому что после вызова конструкторов всех баз вызывается конструктор следующего производного класса.
Внутри конструктора B вызывается функция f(), потому что B не имеет f(), но есть f() в базе A, поэтому вызывается A::f()
, следовательно, вторая строка вывода.
Внутри конструктора B печатается следующая строка вывода B Constructor
.
Затем выполняется конструктор C.
Внутри конструктора C выполняется функция f(), поскольку f() содержится внутри класса C, поэтому она вызывается и печатает четвертую строку C::f()
.
Затем внутри конструктора C печатается пятая строка C Constructor
.
Затем внутри строки A *p=new C;
созданный указатель C*
приводится и присваивается (=
) A*
, потому что любой производный класс может привести свой указатель к любому базовому классу.
p->g()
вызывается, она вызывает A::g()
, которая вызывает виртуальную f(). Любой вызов виртуальной функции вызывает наиболее производный переопределенный вариант этой функции, поэтому A::f()
фактически вызывает C::f()
, что выводит шестую строку C::f()
.
Затем указатель p
удаляется внутри строки delete p;
, следовательно, класс C
уничтожается. Когда объект разрушается, его деструкторы вызываются от самого производного к самому базовому классу, что противоположно порядку конструкторов.
Поскольку деструкторы являются виртуальными, поэтому при уничтожении A*
на самом деле вызывается деструктор ~C()
. Потому что виртуальные функции вызываются в самом производном классе, который переопределяет эту функцию.
Когда вызывается деструктор ~C(), внутри него вызываются три деструктора в следующем порядке: ~C(), ~B(), ~A(). Таким образом, деструкторы вызываются в порядке от наиболее производного к наиболее базовому классу. т.е. обратный порядок конструктора.
Деструкторы C/B/A выводят последние три строки C Destructor
, B Destructor
, A Destructor
.
твой 12-й. Вы сказали, что когда вызывается destruct A*, ~C, но печатается деструктор. Почему?
@Abner В 12) я сказал, что вызывается ~C(), но потом я говорю, что вызываются деструкторы всей иерархии, то есть ~C(), затем ~B(), затем ~A(), также в 13) я говорю это больше явно. т.е. при построении C() вызываются три конструктора в этом порядке A(), B(), C(). Но при разрушении деструкторы вызываются в обратном порядке C(), B(), A(), который показан в последних 3 выходных строках. Поэтому вы должны помнить, что если какой-либо объект построен, вызывается не только его конструктор, но и все конструкторы всей структуры наследования, т.е. C(), B(), A() вызываются три конструктора, одинаковые для разрушения, но в противоположном порядке
Я понял, 3Q!!!
A *p=new C,
может быть скомпилирован, потому что C
является производным от (или наследуется от) A
. Это то что
class C:public B
class B:public A
означает. C
происходит от B
, которое происходит от A
.
Это означает, что каждый объект C
содержит внутри себя объект B
, а каждый объект B
содержит внутри себя объект A
. Вот почему вы видите результат, который видите. Когда вы создаете объект C
, он также создает объект B
внутри него, а это также создает объект A
внутри объекта B
.
Это также означает, что можно автоматически преобразовать указатель C*
в указатель B*
и указатель B*
в указатель A*
. Вот почему код компилируется.
Это основная информация о наследовании классов. Я не знаю, откуда вы взяли этот код, но он должен был объяснить это. Возможно, вам нужно купить хорошую книгу по C++.
Еще одна путаница, на которую, возможно, стоит обратить внимание, заключается в том, что конструктор B
вызывает A::f
, потому что объект C
не (полностью) построен в этой точке, в то время как конструктор C
вызывает C::f
, как и A::g
.
Это один из способов достижения полиморфизма в C++