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


相关推荐
Python×CATIA工业智造1 小时前
Frida RPC高级应用:动态模拟执行Android so文件实战指南
开发语言·python·pycharm
十五年专注C++开发2 小时前
CMake基础:条件判断详解
c++·跨平台·cmake·自动化编译
我叫小白菜2 小时前
【Java_EE】单例模式、阻塞队列、线程池、定时器
java·开发语言
狐凄2 小时前
Python实例题:基于 Python 的简单聊天机器人
开发语言·python
weixin_446122463 小时前
JAVA内存区域划分
java·开发语言·redis
悦悦子a啊3 小时前
Python之--基本知识
开发语言·前端·python
QuantumStack4 小时前
【C++ 真题】P1104 生日
开发语言·c++·算法
天若有情6735 小时前
01_软件卓越之道:功能性与需求满足
c++·软件工程·软件
whoarethenext5 小时前
使用 C++/OpenCV 和 MFCC 构建双重认证智能门禁系统
开发语言·c++·opencv·mfcc
代码的奴隶(艾伦·耶格尔)5 小时前
后端快捷代码
java·开发语言