c++对象模型研究5:构造、解构、拷贝

 

纯虚函数

虚函数是为了重载和多态的需要,子类中可以重写或不重写该函数;纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像Java中的interface。

 

纯虚函数引入原因:
在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。纯虚函数就是基类只定义了函数体,没有实现过程,定义方法如: virtual void Eat() = 0; 不要在cpp中定义;纯虚函数相当于接口,不能直接实例化,需要派生类来实现函数定义;

 

纯虚函数不能调用的两处地方

1.在基类的构造函数中调用纯虚函数。

2.在基类的析构函数中调用纯虚函数。

 

一些原则

纯虚函数的存在:一般来说不要把虚析构函数写成纯虚析构函数。

虚函数的存在:一般而言,把所有成员函数都声明成虚函数,然后再靠编译器优化操作吧非必要的virtual invocation去除,并不是好的设计观念。
虚函数中的const:因为不确定子类会不会更改数据,所以虚函数最好不要声明成const了。

 

无继承情况下的对象构造

三种对象的产生方式

 

Point global;//全局内存配置,生命周期等同整个程序的生命周期 
Point foobar() 
{
	Point local;//局部内存配置,生命周期在{}之间	
	Point *heap=new Point;//堆内存配置,生命周期在new和delete之间,但在此处指针*heap的生命周期在{}之间 
	*heap=local;
	delete heap;
	return local;
} 

程序表现出

1.global内存配置

在C中,global被视为一个"临时性的定义",因为它没有明确的初始化操作,一个"临时性的定义"可以在程序中发生多次.那些实例会被链接器折叠起来,只留下单独一个实体,被放在程序data segment中一个"特别保留给为初始化的global objects使用"的空间。

C++并不支持"临时性的定义",这是因为 class 构造行为的隐含应用的缘故.global在C++中被视为完全定义。C++中所有全局对象都被当作"初始化过的数据"来对待。

2.local内存配置

3.heap内存配置

 

无继承情况下的对象构造。plain old data约等于bitwise copy semantic,他们的构造函数是trivial,要不就是根本没有被构造,要不就是构造了也没有被调用。复制构造函数、析构函数也都是类似的情况。

 

继承体系下的对象构造

A.继承情况下对象的构造过程

 

a. 调用所有的虚基类构造函数,从左到右,由最深到最浅(虚基类在对象模型中是以独特的方式(固定部分与共享部分)支持的,不涉及到在对象模型中的偏移量的问题)

b. 调用所有的上一层的基类构造函数,以基类的声明顺序为顺序(这是因为一般基类的子对象都会被放在对象的开始,并且按基类声明的次序放置)

c. 如果类对象有虚函数表指针,设定其初值,指向适当的虚函数表

d. 如果有一个成员对象并没有出现在成员初始化列表中,且它有一个默认构造函数,那么该默认构造函数必须被调用

e. 记录在成员初始化列表中的数据成员初始化操作会被放在构造函数的函数本身,并以成员声明的顺序为顺序。

f. 程序员自己的代码(在此步以上的操作均为编译器安插的)

 

 

B.继承情况下对象构造过程中如何压制虚基类的构造函数的重复调用
“virtual base class constructors的调用”有着明确的定义:只有一个完整的classobject被定义出来时,它才会被调用;如果object只是某个完整的object的subject,它就不会被调用。

class PVertex 的object中,在保存 PVertex 自己的数据之前,上面有很多个父类的subobject,那么虚基类Point的构造函数不会被其他的subobject所调用,它的构造函数只有当整个object被定义出来时,也即PVertex数据定义出来时才会被调用,也就是只会被PVertex所调用。而在PVertex之前的subobject 对虚基类构造函数的调用操作将会被抑制。另外,如果没有最下层的PVertex,那么就是被Vertex3d调用。综上所述,这样才能保证共享虚基类对象的一致性。

 

 

对象复制

关于重载赋值操作符和复制拷贝构造函数的区别

 

拷贝构造函数是用一个已存在的对象去构造一个不存在的对象(拷贝构造函数毕竟还是构造函数嘛),也就是初始化一个对象。
而赋值运算符重载函数是用一个存在的对象去给另一个已存在并初始化过(即已经过构造函数的初始化了)的对象进行赋值。

 

当设计一个类,并以一个类对象指定给另一个对象时,我们有三种选择:
a.什么都不做,实施默认行为
b.提供一个explicit copy assignment operator
c.拒绝拷贝,拒绝拷贝方式:
1.将copy assignment operator私有化
2.不提供函数定义,导致在链接失败


对象赋值(拷贝)函数是为了打开named value return(NVR)
对象赋值操作copy assignment operator的合成条件和构造函数类似
当不要Bitwise Copy Semantics时,类就需要合成一个对象赋值操作:
1.当类内含一个成员对象,而成员对象声明有一个copy constructor operator时
2.当类继承一个基类对象而后者存在有一个copy constructor operator时
3.当类声明了一个或多个virtual functions时,
4.当类派生自一个继承串链,其中有一个或多个virtual base classes时。此时无论基类有没有copy operator。

 

 

解构

如果类没有定义析构函数,那么只有类存在成员对象且该成员对象含有析构函数的情况下,编译器才会自动合出一个析构函数来。默认情况下编译器并不会合成一个析构函数,即使是它拥有一个virtual function。
析构函数的扩展方式与构造函数相同,但顺序相反。

 

 

参考:

《深度探索C++对象模型》

http://blog.csdn.net/ChinaJane163/article/details/50119625

posted @ 2018-08-17 17:44  _raindrop  阅读(137)  评论(0编辑  收藏  举报