【C++对象模型】构造函数III

构造函数语意学

》》构造函数语意学I---默认构造函数的构造操作《《

》》构造函数语意学II---拷贝构造函数的构造操作《《

》》构造函数语意学III---程序转化语意学《《

程序转化语意学

显式的初始化操作

有这样的定义

cpp 复制代码
X x0;
X x1(x0);
X x2 = x0;
X x3 = X(x0);

必要的程序转化有两个阶段:

  1. 重写每一个定义,其中的初始化操作会被剥除。
  2. class的拷贝构造调用操作会被安插进去。

参数的初始化

C++标准(Section 8.5)说,把一个class object当做参数传给一个函数(或是作为一个函数的返回值),相当于以下形式的初始化操作

cpp 复制代码
X xx = arg;

其中xx代表形式参数(或返回值)而arg代表真正的参数值。

若已知这个函数以及下面这样的调用方式

cpp 复制代码
void foo(X x0);
X xx;
foo(xx);

在编译器实现技术上,有一种策略是导入所谓的临时性 object,并调用拷贝构造将它初始化,然后将此临时性object交给函数。例如将前一段程序代码转换如下:

cpp 复制代码
X __temp0;
__temp0.X::X(xx);
foo(__temp0);

临时性object先以class X的拷贝构造正确地设定了初值,然后再以bitwise方式拷贝到x0这个局部实例中。噢,真讨厌,foo()的声明因而也必须被转化,形式参数必须从原先的一个X对象改变为一个X引用,像这样:

cpp 复制代码
void foo(X&x0);
其中class X声明了一个析构函数,它会在foo()函数完成之后被调用,对付那个临时性的object。

另一种实现方法是以"拷贝建构"(copy construct)的方式把实际参数直接建构在其应该的位置上,此位置视函数活动范围的不同,记录于程序堆栈中。

返回值的初始化

cpp 复制代码
X bar(){
    X xx;
    ...
    return xx;
}

bar()的返回值如何从局部对象xx中拷贝过来

  1. 首先加上一个额外参数,类型是 class object的一个引用。
  2. 在 return指令之前安插一个拷贝构造调用操作,以便将欲传回之object的内容当做上述新增参数的初值。

现在编译器必须转换每一个bar()调用操作,以反映其新定义。

cpp 复制代码
X xx = bar();
    |
    |
    V
X xx;
bar(xx);

在编译器层面做优化

在一个像bar()这样的函数中,因此编译器有可能自己做优化,方法是以result参数取代named return value。

cpp 复制代码
void bar(X &__result){
    //构造函数被调用
    __result.X::X();
    ...//直接处理__result
    return;
}

这样的编译器优化操作,有时候被称为Named Return Value(NRV)优化。NRV优化如今被视为标准C++编译器的一个义不容辞的优化操作------虽然其需求其实超越了正式标准之外。

有的个程序的第不能实施 NRV 优化,因为 test class 缺少一个拷贝构造。

虽然NRV优化提供了重要的效率改善,但它还是饱受批评。

想象你已经摆好了你的拷贝构造的阵势,使你的程序"以copying方式产生出一个object时",对称地调用析构

cpp 复制代码
void foo{
    //这里希望有个拷贝构造
    X xx = bar();
    ...
    //这里调用析构
}

在此情况下,对称性被优化给打破了;程序虽然比较快,却是错误的。

Copy Constructor:要还是不要?

cpp 复制代码
class Point3d{
public:
    point3d(float x , float y , float z);
private:
    float _x, _y, _z;
}

这个class的设计者应该提供一个显式拷贝构造吗?

答案当然很明显是no。没有任何理由要你提供一个拷贝构造函数实例,因为编译器自动为你实施了最好的行为。比较难以回答的是,如果你被问及是否预见class需要大量的memberwise初始化操作,例如以传值(by value)的方式传回objects?如果答案是yes,那么提供一个构造的显式inline函数实例就非常合理------在"你的编译器提供NRV优化"的前提下。

成员们的初始化队伍(成员初始化列表)

在下列情况下,为了让你的程序能够被顺利编译,你必须使用成员初始化列表

  1. 当初始化一个 reference成员时;
  2. 当初始化一个 const成员时;
  3. 当调用一个 base class的构造,而它拥有一组参数时;
  4. 当调用一个 member class的构造,而它拥有一组参数时。
  • list内部的真正操作是什么

编译器会一一操作初始化列表,以适当顺序在构造之内安插初始化操作,并且在任何显示用户代码之前。

事实上,有一些微妙的地方要注意:list中的项目顺序是由class中的members声明顺序决定的,不是由初始化列表中的排列顺序决定的。


相关推荐
草莓熊Lotso6 分钟前
【C++ 基于微服务的即时通讯系统】etcd + brpc 从安装到服务注册发现全链路实战
网络·数据库·c++·redis·微服务·etcd
Wang's Blog5 小时前
Java 项目实战: 交友项目:登录链路回顾与今日任务地图
java·开发语言·状态模式·交友
砚底藏山河6 小时前
python量化:行情数据回溯
java·开发语言·python·金融·maven
今年下半年6 小时前
AI智能体项目实战记录(02):初始化 Python 项目
开发语言·人工智能·python·fastapi
Williamtym6 小时前
PickGo Lab实践:用浏览器、Python和MCP串起机器人搬运仿真
开发语言·python·机器人
DongQiShanRen6 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
CS创新实验室6 小时前
Python 对象“死“了之后去了哪?——引用计数与分代垃圾回收
开发语言·jvm·python
quantdash_cc7 小时前
Pandas 处理分钟级股票行情:从原始数据清洗到 K 线聚合
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
yu_zhidun7 小时前
企业电脑监控软件怎么选:别只看功能清单,先看这 6 个底层能力
开发语言·电脑·php·域智盾系统
把我格子衫拿来7 小时前
VTK 9.5 与 Qt 6 联合开发:从嵌入三维窗口到可维护的桌面架构
c++·python·qt·vtk·三维可视化