假设有如下代码:
class A
{
public:
A(){cout<<"A constructed"<<endl;}
~A(){cout<<"A delete"<<endl;}
};
class B:public A
{
public:
B(int v=1):p(v){cout<<"B constructed"<<endl;}
~B(){cout<<"B delete"<<endl;}
int p;
};
class C:public B
{
private:
B b;
public:
C(){cout<<"C constructed"<<endl;}
C(B x):b(x){cout<<"C 的B被赋值"<<endl;}
~C(){cout<<"C delete"<<endl;}
};
现在有如下变量创建:
B b1;
我们来看看结果:
A constructed
B constructed
B delete
A delete
这是为什么呢?
1、构造函数的调用顺序:自上而下
当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达最底层目标派生类的构造函数。
也就是说,不过基类有没有成员,都会执行它的构造函数。所以开始是A constructed
,其次执行B类自己的构造函数,即:B constructed
.
2、析构函数的调用顺序:自下而上
当删除一个对象时,首先调用该派生类的析构函数,然后调用上一层基类的析构函数,依次类推,直到到达最顶层的基类析构函数
故而,删除的时候,从b1这个变量自己开始调用析构函数,即B delete
,在调用基类的析构函数,即A delete
.
代码如下:
C c;
结果是:
A constructed
B constructed
A constructed
B constructed
C constructed
C delete
B delete
A delete
B delete
A delete
这是为什么呢?
首先一点,成员初始化的次序是类定义中声明成员的次序。
也就是说,先按声明的顺序,对当前类C的成员变量进行初始化,而当前C的成员变量只有一个即: B b; 由上面的示例我们知道,初始化它的时候,的构造函数的执行顺序是:
A constructed
B constructed
其次,我们需要构造变量C c本身,同样根据上面的示例,知道是:
A constructed
B constructed
C constructed
由此构造函数调用完毕。
那么析构函数呢,则是跟构造函数相反的方向。先对c变量析构,所以有:
C delete
B delete
A delete
其次对c变量的成员变量 b进行析构,也就是:
B delete
A delete
由此,执行顺序搞定。
假设代码有:
B b1;
C c(b1);
那么其结果是:
A constructed
B constructed
A constructed
B constructed
C 的B被赋值
B delete
A delete
C delete
B delete
A delete
B delete
A delete
B delete
A delete
咋们来捋一捋:
首先创建 B b1;对象,有:
A constructed
B constructed
其次,创建 C c(b1);由于带参数,所以不用默认构造函数了,执行带参数的构造函数,所以有:
A constructed
B constructed
C 的B被赋值
来看析构函数,析构函数的执行顺序是,最后一个类变量(这里 是c)先析构,然后在析构b变量;其实就是按在程序当中声明的反顺序来的。
故而这里先析构c变量:
C delete
B delete
A delete
但是我们发现程序运行结果不是这样,这是为什么呢?
这是因为需要先析构 C的构造函数当中C(B x) 的B x这个临时变量,所以先是:
B delete
A delete
再是c变量自己:
C delete
B delete
A delete
再是 c变量里面的类成员 b:
B delete
A delete
最后是主程序中的 B b1变量的析构调用:
B delete
A delete
由此析构函数执行完毕。
假设在B类中,有:
B(int v=1):p(v){cout<<"B constructed"<<endl;}
那么当主程序中创建变量时:
B b1;
将会执行上面那个构造函数,所以b1.p=1(为了方便,这里对p使用了公开)。
如果同时写:
B(){cout<<"B constructed"<<endl;}
B(int v=1):p(v){cout<<"B 缺省constructed"<<endl;}
这样的话,编译是错误的,因为两个构造函数,导致编译器不知道调用哪个。