【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声明顺序决定的,不是由初始化列表中的排列顺序决定的。


相关推荐
獨枭1 小时前
CMake 构建项目并整理头文件和库文件
c++·github·cmake
西猫雷婶1 小时前
python学opencv|读取图像(十九)使用cv2.rectangle()绘制矩形
开发语言·python·opencv
liuxin334455662 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
码农W2 小时前
QT--静态插件、动态插件
开发语言·qt
ke_wu2 小时前
结构型设计模式
开发语言·设计模式·组合模式·简单工厂模式·工厂方法模式·抽象工厂模式·装饰器模式
code04号2 小时前
python脚本:批量提取excel数据
开发语言·python·excel
小王爱吃月亮糖2 小时前
C++的23种设计模式
开发语言·c++·qt·算法·设计模式·ecmascript
hakesashou3 小时前
python如何打乱list
开发语言·python
网络风云3 小时前
【魅力golang】之-反射
开发语言·后端·golang
Want5954 小时前
Java圣诞树
开发语言·python·信息可视化