深度探索c++对象模型第二章读书笔记

2.2 Copy Constructor 的建构操作

有三种情况,会以一个object的内容作为另一个class object的初值。

  • 1、对一个object做明确的初始化操作时;
  • 2、当object被当做参数交给某个函数时;
  • 3、当函数传回一个class object时。

如果程序员明确定义了一个copy construct函数,那么在大多数情况下,当一个class object以另一个实体作为初值时,copy construct函数会被调用,但这可能会导致一个暂时性class object的产生或程序代码的蜕变(或两者都有)。

而如果 ** class没有明确提供一个explicit copy constructor **时,当class object 以“同一类型的另一个object”作为初值时,其内部是 采用default memberwise initialization(深拷贝?) 完成的,也就是将每一个内建的或派生的data member的值,从某个object拷贝一份到另一个object上。不过这并不会拷贝其中的成员类对象,而是以递归的方式实施memberwise initialization。

Default constructors 和copy constructors 在必要的时候才有编译器参数出来。

有四种情况class不会展现出“bitwise copy semantics”

  • 1、当class内含一个member object 而后者的class声明有一个copy constructor时(不论后者的class是显示的声明还是被编译器合成都可以)
  • 2、当class继承自一个base class而后者存在一个copy constructor时(同样,不论是被显示声明还是被合成都可以);
  • 3、当class声明了一个或多个virtual functions时;
  • 4、当class派生自一个继承串链,其中有一个或多个virtual base classes.

这里的意思是-----除了上面这四种情况,其他情况都会用到bitwise 吗???

2.3程序转换语意学

c++中函数的参数初始化,把一个class object 当做参数传给一个函数(或是作为一个函数的返回值),相当于以下形式的初始化操作:
在这里插入图片描述
上面的调用方式会要求局部实体x0以MemberWise的方式将xx当做初值,在编译器实现技术中,有一种策略是** 导入所谓的暂时性object,并调用copy construct将它初始化,然后将暂时性object交给函数 **。所以上面函数的调用方式可以展开为:
在这里插入图片描述
在这里插入图片描述

存在一个问题:

这里会产生暂时性的Object先以class X的copy constructor正确地设定初值,然后再以bitwise方式拷贝到x0这个局部实体中。 (这里为什么会采用bitwise的方式?)

所以为了避免这个问题,foo函数的声明必须被转化,形式参数必须从原先的一个class X object改变为一个class X reference。 比如:

void foo(X& x0);

已知下面这个函数定义:

X bar()
	{
		X xx;
		// 处理 xx ...
		return xx;
	}

这里bar()的返回值会从函数的局部对象xx中拷贝到出来。

  • 1、首先加上一个额外的参数,类型是class object 的一个reference 。这个参数被用来放置“拷贝建构”而得的返回值;
  • 2、在return指令之前安插一个copy constructor 调用操作,以便将欲传回值object的内容当做上述新增参数的初值。
    最后一个操作会重新改写函数,使函数不返回任何值。
    根据以上信息,bar()转换如下:
///函数转换
///用来反映copy constructor的应用
//c++ 伪代码
void 
bar(X &__result)  //  加了一个额外的参数
{
	X xx;

	//编译器产生的default constructor调用操作
	xx.X::X();

	//对xx进行处理操作
	
	//编译器所产生的copy constructor调用操作
	__result.X::X(xx);

	return ;
}
在编译器层面上的优化

在一个如bar()这样的函数中,所有的return指令传回相同的具名数值(named value),因此编译器有可能自己做优化,方法是以result参数取代named return value。

这样的编译器优化操作被称为Named Return Value(NRV)优化

在一个自定义类型,它既没有任何member(或base)class objects带有copy constructor,也没有任何的virtual base class 或virtual function。所以,默认情况下该类型的"memberwise"初始化操作会导致“bitwise copy”。
在这里插入图片描述
这在个类型中,三个坐标成员是以数值来储存。bitwise copy既不会导致memory leak,也不会产生address aliasing(地址冲突),因此它即快速又安全。所以这个class的设计者就没有必要提供一个explicit copy constructor。因为编译器已经提供了最好的选择。而如果我们可以预见的是class需要大量的memberwise初始化操作,例如以传值的方式传回objects,那么提供一个copy constructor的explicit inline 函数实体就很有必要了。

在初始化时,有时可以使用memcpy()和memset()可以使得程序运行更有效率,但这需要在掌握c++ object Model的语义学知识基础上,不然可能会出大问题。
在这里插入图片描述
在这里插入图片描述

copy constructor的应用,会使得编译器或多或少的对我们的程序代码进行了部分转化,尤其是在使用一个函数以传值(by value)的方式传回一个class object,而该class有一个copy constructor (明确定义的或隐式合成的)时,这都会导致深奥的程序转化。此外编译器也将copy constructor 的调用操作优化,以一个额外的第一个参数(数值被直接存放其中)取代NRV。

2.4成员们的初始化队伍

当在类中写下一个constructor时,我们有计划设定class members的初值。由member initialization list,或在constructor函数本身之内。
有四种情况,我们为了使程序能够被顺利编译,必须使用member initialization list:

  • 1、当初始化一个reference member时;
  • 2、当初始化一个const member 时;
  • 3、当调用一个base class 的constructor ,而它拥有一组参数时;
  • 4、当调用一个member class的constructor ,而它拥有一组参数时。

在这里就会有个困惑,为什么必须要使用member initialization list,而member initialization list在程序运行过程中到底会发生什么事情?
编译器会一一操作initialization list,以适当次序在constructor之内安插初始化操作,并且在任何explicit user code之前
在这之中还有一些微妙的地方需要注意:list中的项目次序是由class中members __ 声明次序 __ 决定,不是由initialization list中的排列次序决定的。
初始化次序和initialization list 中的项目排列次序之间的外观错乱,可能会引发意想不到的错误。

简而言之:编译器会对initialization list 一一处理并可能重新排序,以反映出members的声明次序,它会安插一些代码到constructor体内,并置于任何explicit user code 之前。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值