分享
 
 
 

C++特性探寻-构造函数和析构函数

王朝c/c++·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

构造函数提供了一种机制,通过它有机会完成必要的初始化工作,从而使对象成为有意义

的存在物,而不仅仅只是一块原始的空间。

但是,我们逐渐了解到,构造函数具有的地位,不仅对于用户(程序员),对于编译器履

行职责也极为重要。通过这个机制,它让C++的一些基本的特性,如继承、多态得到了正确

的贯彻和表现。

首先不难理解的一点是在构造函数中,要确保基类对象的正确构造,如果是从基类继承的

话。因为继承类对象至少可以被“低”看为一个基类对象,具有后者的所有行为和表现,

所以基类的构造函数首先被调用。如果所谓的基类也是从其他类继承过来的,这就形成了

一个调用链。最后的情况是,最基础的类的构造函数首先被执行,然后才是上一层的构造

函数,如此到最外层的继承类。这个过程必须是严格有序的。如果没有这个次序保证,继

承类就有机会在基类还没构建好的情况下就访问基类的数据或函数,这将导致不可预料的

、灾难性的后果。

只有首先确保基类的正确构造,接下来才能进行继承类本身的构造。因为类中可能含有成

员对象,必须保证这些对象也被构造,按照一定的次序(一般就是变量声明的次序)。可

能其中一个或一些成员对象的构造需要参数,这需要在类的构造函数中提供所有需要的参

数(构成所谓显式的“初始化列表”)。

看起来这就是构造函数的全部隐含(或半隐含)工作?不是的。我们忽视了一个极为重要

的东西,我称之为类关联信息(表)。类关联信息是编译时生成的、为运行时所需的类的

附加数据(可以认为这些数据放在全局数据区中)。我们熟悉的虚函数表(v-table),我

把它归在类关联信息的范畴之中(最初,我以为虚函数表就是全部,但这个理解有点狭义

)。此外,还包含运行时类型信息(RTTI)。此外,不排除我们还不清楚的其它辅助数据

(总之,类关联信息是一种广义的、统一的称呼)。如果编译器为类生成了类关联信息,

那么毫无疑问,必须在构造函数中将它与当前对象(类的实例)关联起来(也许简单到只

需要设置一个指针即可)。

例如,如果类中含有虚函数,或者,它覆盖了基类中的虚函数(两种情况下都意味着类有

自己的虚函数表)。那么,设置正确的关联后,将存在一个指针(v-ptr),它正确地指向

了该虚函数表(v-table)。此后,多态才能表现出所期望的正确行为。

再次指出(次序的重要性),设置关联,或者狭义地说,设置v-ptr必须发生在对基类构造

函数的调用之后。因为,继承类如果有自己的虚函数表,那么v-ptr会被改写,以指向该表

,即使此前v-ptr已经被基类所设置。这是合法的,也正是所期望的。但是语义上我们绝不

允许基类可以改写继承类所设置的v-ptr。如果v-ptr设置发生在基类构造调用之前,那么

这种非法的一幕就会发生。

所有上述的事情完成之后(对于用户来说它们几乎是隐含的),才真正开始执行用户的初

始化代码。

在构造函数中调用虚函数,会发生什么?发生的情况也许是始料未及的。当前类的对象正

在构建,v-ptr指向的是当前类的虚函数表。此时,还没到继承类执行它自己的代码(如设

置v-ptr,执行初始化代码)的时候,那一切发生在当前类的构造函数执行完毕之后。所以

,将要执行的是本类的虚函数版本,而不是可能被覆盖的继承类的版本。这里有一个反面

的证据。假如v-ptr设置发生在基类构造函数调用之前,让我们有机会调用继承类的虚函数

版本,这意味着什么?继承类还没有完成初始化(因而对象还没有构建好),我们企图在

一个没有构建好的对象上执行它的成员函数,可以想见后果是灾难性的。

类的构造函数何时被调用?在对象被创建的时候。对象可能位于栈上,全局数据区,或堆

上。对象可能会在声明的地方创建,这样的对象位于栈上或全局数据区。对象也可以使用

new操作符动态地创建,这样的对象将位于堆上。

析构函数

析构函数提供了一种和构造函数相反的机制,允许在销毁一个对象之前(亦即回收对象所

占用的空间),让对象释放自己所使用的资源。再次,这里所关心的是语言底层所发生的

事情。

与构造函数的执行次序刚好相反,析构函数从最外层的类开始执行,最基础的类的析构函

数最后执行,看起来就象一层层的剥壳。这个次序要得到严格保证的理由也是明显的(违

反次序又将产生相关性问题)。结果看起来是这样的:用户析构代码->成员对象析构->基

类析构函数。

析构函数在对象行将被销毁时调用。当对象超出其作用域,例如一个函数内部的局部变量

,在函数返回时将被自动销毁。对于在堆上创建的对象,我们只能通过对象指针p,执行d

elete p来销毁它。现在就有一个问题。p指向了某个类型(例如A)的对象,但是却可能是

通过上溯造型(upcast)得来的,因此它指向的实际上是一个B的对象,那么将发生什么?

显然,正确的做法是把对象作为B的实例来销毁。也就是说,调用B的析构函数。我们也许

很快想到,首先,在运行时刻可以知道这个对象“实际上”是什么类型(那个最晚的派生

类,本例中假定就是B),例如通过RTTI。然后,根据实际的类型,“找到”并调用该类的

析构函数。然而,这个想法不会自然实现,需要在编译时把类的类型信息和一个指向析构

函数的指针关联起来。不过,上述考虑似乎复杂了点。我们可以把这个析构函数指针放入

虚函数表中(某个特别的位置)。理由是,与虚函数非常相似,存储这个析构函数指针的

slot可以为继承类所覆盖(从而指向继承类的析构函数),而且继承类应该总是覆盖它(

然而下面将看到,实际的情况与“总是覆盖”有出入)。

无论怎样,析构函数指针是存储在前面称之的类关联信息(表)中,编译器不难找到。因

而当通过对象指针(即使已经上溯造型)执行删除操作时,总是能够调用正确的析构函数

。但是,在多个编译器的试验发现,只有把基类的析构函数声明为虚的,继承类才会覆盖

它。看来的确采用虚函数的技术来对待析构函数了。如果基类的析构函数没有声明为虚的

,则执行前面的delete p时,只有A的析构函数得到执行,而B的没有执行!

这是令人惊讶的。竟然允许“不完全”析构的情况发生,把选择权以及责任交给了程序员

!我不能确定这么做的主要理由是什么。也许还是因为C++如此看重效率,它迫使程序员在

必要的时候显式声明他的需求,因为类似虚函数的间接调用要占用额外的空间和时间。

前面考察了虚函数在构造函数中的行为,继续来看一下在析构函数中的情形。因为析构过

程是自外向里的,当前类在调用虚函数的时候,其继承类此前已经完成了析构,不再是可

用的。因而,是不可以调用继承类的虚函数版本的。结果,调用的仍然是本地版本(和构

造函数中的结论一样,虚函数机制被忽略)。

Note: 构造/析构函数中虚函数的行为,我又一次从“Thingking in C++”中看到的。Tha

nks to Bruce Eckel!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有