您的位置:首页 > 编程语言 > C语言/C++

关于C++继承体系中类的构造与析构的顺序【转贴】

2007-03-31 16:31 363 查看

关于C++继承体系中类的构造与析构的顺序
1.构造函数
先看下面的类定义
class B1 class B2
{ {
public: public:
int i; int i;
B1() { i = 0; } B2() { i = 0; }
virtual void f() {} virtual void f() {}
} ; } ;

class M1 class M2
{ {
public: public:
int i; int i;
M1() { i = 0; } M2() { i = 0; }
virtual void mf() {} virtual void mf() {}
} ; };

class C : public B1, public B2
{
public:
virtual void f() {}
M1 m1;
M2 m2;
};

(1)编译器会不会为C生成默认构造(default ctor)
编译器只在需要时才会为一个类生成def ctor。“需要时”指:
a.一个类有虚函数
b.一个类有虚基类
c.一个类的基类有def ctor
d.一个类的成员类有def ctor
在这里,class C符合c,d两个条件,编译器会为它生成def ctor.

(2)默认构造的内容
编译器生成的def ctor是这样的
C::C()
{
B1::B1()
B2::B2()
设定虚表指针指向C的虚表
m1::M1()
m2::M2()
}
a.按声明顺序构造基类
b.设定虚表指针
c.按声明顺序构造成员类

(3)对自定义构造函数的改造
对于B1,B2,M1,M2,已有构造函数,但编译器会对其改造,加入一些代码,完成必要的初始化工作。改造后的ctor如下:(以B1为例)
B1::B1()
{
设定虚表指针指向B1的虚表
i = 0;
}
a.设定虚表指针
b.如果用户写有代码(如i=0),则执行这些代码

(4)综合(2),(3),构造函数完整代码如下
T::T()
{
按声明顺序构造基类
设定虚表指针
按声明顺序构造成员类
如果用户写有代码,则执行这些代码
}
在用户代码执行前,基类与成员类已构造完毕,虚指针已设定。

所以C的构造顺序是:B1-〉B2-〉虚指针设定-〉m1-〉m2->C自己的代码(如果有的话)
由(3)可知,每个类的ctor中,都会暂时将虚指针指向自己的虚表。调用B1::B1时,虚指针指向B1的虚表,然后在C::C中,虚指针被重新设定为C的虚表。
在继承体系中,构造是由上而下的,B1或B2构造时,C还未构造。C构造时,B1和B2已构造完成。

所以在继承体系中,一个类的构造函数执行时,该类的所有基类已构造完毕,虚指针已设定,所有成员类也已构造完毕。但该类的所有子类均未构造完成。

2.析构函数
(1)编译器会不会为C生成析构函数(dtor)
编译器只在需要时才会为一个类生成dtor。“需要时”指:
a.类的基类有析构
b.类的成员类有析构
与构造不同,少了两点,因为在一个类析构时,这是一个破坏操作(destory),既然类以已经没用了,何必再把虚指针设一下呢。

(2)析构的内容
编译器生成的析构是这样的
C::~C()
{
m2::M2()
m1::M1()
B2::B2()
B1::B1()
}
a.按声明顺序相反的顺序析构成员类
b.按声明顺序相反的顺序析构基类
这里的相反顺序是C++标准规定的要求,不同编译器都应做到,否则不符合标准(实际上编译器或多或少都有不符标准的地方,当然不是指这里所说的顺序问题)

(3)完整的析构的调用顺序
对于已有析构函数的类,编译器会对其改造,加入一些代码,完成必要的工作。改造后的dtor如下:
T::~T()
{
设定虚表指针指向T的虚表
执行用户代码
按声明顺序相反的顺序析构成员类
按声明顺序相反的顺序析构基类
}
这是完整的析构的调用顺序
在用户代码执行前,虚指针已重新设定,以便用户代码能正确执行。然后再析构成员类和基类。

所以,如果上面例子中的五个类都有析构函数的话,调用顺序是:
虚指针设定-〉C自己的代码->m2-〉m1-〉B2-〉B1
每个类的dtor中,都会暂时将虚指针指向自己的虚表。调用C::~C时,虚指针指向C的虚表,然后在B1::~B1中,虚指针被重新设定为B1的虚表。

可见,在继承体系中,析构是由下而上的,B1或B2析构时,C已被析构。C析构时,B1和B2还未被析构。
所以在继承体系中,一个类的析构函数执行时,该类的所有基类还未被析构,所有成员类也未被析构。但该类的所有子类均已析构完成。

3.虚函数
在构造或析构中调用虚函数,会怎样.如:
B1::B1()
{
f();
}
调用的是B1::f()还是C::f()
答案是B1::f()
每个类的ctor中,都会暂时将虚指针指向自己的虚表。调用B1::B1时,虚指针指向B1的虚表
所以是B1::f()
如果在C的构造函数中调用f(),那就调用C::f()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: