第二章:深度探索C++ 对象模型

1:在C++的构造函数上,往往编译器会做很多你不知道的事,就比如说构造函数的隐式转换,虽然有时候很方便,但是在某些情况下会造成一些意想不到的情况发生,explict关键词就因此而生~!

2:当编译器需要时,才会合成一个default constructor出来(默认构造函数)。被合成出来的默认构造函数只能执行编译器所需的行动,它不会满足程序本身的需要。下面讨论四种必须为class合成一个构造函数的情况

(1)当一个class没有任何的构造函数,但其内部含有一个member class object 类对象,且该后者类有一个默认的构造函数时,那么就必须为该类合成一个构造函数(注意:合成的操作在构造函数真正需要被调用时才会发生,它会调用每一个member class object的默认构造函数)
(2)当一个class没有任何构造函数,且该class派生自一个带有“default constructor”的基类时,那么编译器会为这个派生类自动合成一个默认构造函数(该默认构造函数会自动调用base class的默认构造函数)
(3)当一个class没有任何构造函数,且该class内有virtual functions时(或是其基类中含有虚函数),那么编译器会为这个类自动合成一个默认构造函数(这些构造函数中会安插一些代码,对vptr进行初始化,让它指向正确的vbtl)
(4)当一个class没有任何构造函数,且该class继承自一个虚基类时,编译器会为这个类自动合成一个默认构造函数(这些构造函数中会安插一些代码,使得每一个虚基类的执行期存取操作得以实现)

3:合成的构造函数之所以被合成出来,是因为其有自身的任务

	(1)调用成员对象或是基类的默认构造函数
	(2)为每一个类对象初始化其vptr或是其虚基类机制

4:除去上述的四种情况,即使class没有定义任何的构造函数,编译器也不会为其合成一个默认构造函数

5:合成的默认构造函数中,只有基类类对象和成员类对象会被初始化,所有其他的非静态成员数据都不会被初始化(包括整数、整数指针、整数数组等等),因为这是编译器所不需要的操作。

6:编译器合成的默认构造函数不会为其每一个数据成员赋初始值~

7:拷贝构造函数出现在以下三种情况之中

(1)显式的以一个object的内容作为另一个类对象的初值  X xx = x;
(2)以一个object作为一个函数的参数 foo(xx);
(3)当函数返回一个class object时 return xx;

8:如果一个类没有显式的定义一个拷贝构造函数,那么当需要拷贝时,其内部是以默认逐项初始化手法完成的

(1)每一个内建的或者派生的数据成员的值,普通的拷贝。
(2)对于其中的member class object 会以递归的方式施行逐项初始化

9:当一个类展现出“bitwise copy semantics”(位逐次拷贝)时,则不需要编译器合成默认的拷贝构造函数,具体以下四种情况中,类不会展现出位逐次拷贝的特性~:

	(1)当一个类内含一个类对象成员,且后者的class声明有一个拷贝构造函数时(无论该构造函数时设计者声明String,还是编译器自己合成的Word)
	(2)当一个class继承自一个基类,该基类存在一个拷贝构造函数时(无论是显式声明还是合成所得)
	(3)当class声明了一个或多个的虚函数时
	(4)当class派生自一个继承链,其中有一个或多个虚基类时

可以看出,这四种情况和上面的默认构造函数的情况非常类似,但是不同之处在于,当未出现这四种情况时,不会生成默认构造函数,也就不会对成员进行任何的初始化操作;不生成默认拷贝构造函数时,会进行位逐次拷贝“bitwise copy semantic”

10:对于位逐次拷贝,指针的拷贝为浅拷贝,也就是说,拷贝的指针指向的是同一块内存,当释放了一个指针的内存时,另一个指针指向为空,会造成很大的BUG。其中,当有了虚函数机制时,就有了vptr,对vptr的浅拷贝显然不不正确的,它可能会没有指向正确的vbtl。所以这也就解释了为什么有了虚函数就需要合成的拷贝构造函数,来对vptr进行正确的初始化。

11:在继承链中有虚基类时,编译器必须让虚基类对象的位置在编译器就准备妥当,维护“位置的完整性”是编译器的责任,而位逐次拷贝可能会破坏这个位置,所以我们必须合成一个拷贝构造函数,在其中安插一些代码,以设定虚基类指针或者偏移量(或者只是简单的确定其没有被抹消)

12:综上,在没有显式定义的拷贝构造函数的情况下,合成拷贝构造函数是为了保证一些特殊情况的拷贝正确进行。

13:在严谨的C++定义中,“定义”是指“占用内存的行为”

14:当把一个class object作为一个参数传给函数时(或者作为函数的返回值),在过程中会创建一个临时的object,并调用拷贝构造函数将其初始化,然后将此临时性的对象传递给函数,在函数使用完之后,自动调用destructor销毁临时的对象

15:编译器的优化操作:Named Return Value NRV 现在是C++编译器的一个义不容辞的优化操作。
NRV 保存返回值的变量(也就是上述的临时object)不再使用没名没姓的__temp0这样的东西了,而是直接把c作为返回变量,因此应该将NRV翻译为“有名字的返回变量”。

(1)NRV由编译器默默完成,但是它是否被完成了呢?程序员并不清楚~
(2)一旦函数变得比较复杂,优化也会变得难以施行
			X xx(1024);
			X xx1 = X(1024);
			X xx2 = (X)1024;

上述三种初始化方法语义相同,但是二三种方法却多了一个步骤:将一个临时性的对象设以初值1024,再调用拷贝构造函数将临时性对象作为显式对象的初值,这样的效率负担非常严重。

16:NRV的优化会出现在,函数以传值的方式传回一个类对象,当该类有一个拷贝构造函数时,编译器会对你的代码进行一定的背后优化。

17:成员初始化时,当出现以下四种情况,必须使用成员初始化列表的操作:

(1)初始化一个引用reference成员时
(2)初始化一个常量const成员时
(3)当调用一个base的构造函数,而基类的构造函数拥有一组参数时
(4)当调用一个成员类对象的构造函数时,且该成员类的构造函数拥有一组参数时

18:不使用成员初始化列表方式,若类中有string 类对象,那么该类的构造函数会先产生一个临时性的string对象,然后将它初始化,之后以一个assignment运算符将临时性的对象指定给类成员:string 对象。这样的效率确实是不高~
但是使用了成员初始化列表,构造函数的内部扩张行为就会变为string(0);此时无需上述的繁琐步骤~

19:成员初始化列表中的项目顺序是由class中的成员声明顺序决定的,不是由初始化列表中的排列顺序决定的。

20:成员初始化列表的操作会在任何的显式定义操作之前。

21:可以使用“存在于构造函数体内的一个成员”而不是“存在于成员初始化列表”中的成员来为另一个成员赋值。也可以使用一个成员函数在初始化列表中设定一个成员的初值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N1314N

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值