第2章 构造函数语义学3 : 程序转化语义学

目录

1、显示初始化(Explict Initilization)

2、参数初始化(Argument Initialization)

3、返回值初始化(Return Value Initialization)

4、在使用者层面优化(Optimization at the user level)

5、在编译器层面优化(Optimization at the Compiler level)

6、Copy Constructor: 要还是不要

🍉 什么时候需要?

🍇应该如何实现


1、显示初始化(Explict Initilization)

已知有这样的定义, 下面有三个定义,每一个都明显地以 x0 来初始化其 class object:

X x0;

void foo bar() {
X xl( x0 );   // 译注:定义了 x1
X x2 = x0;    // 译注:定义了 x2
X x3 =X( xO );// 译注:定义了 x3
//..

可能的程序转换,分为以下两步:

  1. 定义被重写,初始化操作被剥除
  2. 编译器安插 X copy construction 的调用操作
// C++ 伪码
void foo bar() {
X xl;// 译注:定义被重写,初始化操作被剥除
X x2;// 译注:定义被重写,初始化操作被剥除
X x3;// 译注:定义被重写,初始化操作被剥除
// 编译器安插 X copy construction 的调用操作
x1.X::x( x0 );
x2.X::;X(x0);
x3.X::X(x0);
//.

2、参数初始化(Argument Initialization)

若已知这个函数,下面这样的调用方式:

void foo( X x0 );

X xx;
//..
foo( xx );

将会要求局部实体(局部实例)x0以成员方式将xx当做初值。在编译器实现技术上,有一种策略是引入临时对象,然后将该临时对象交给函数。可能的程序转换,分为以下三步:

  1. 引入临时变量
  2. 使用拷贝构造对之初始化
  3. 改变函数声明
// C++ 伪码
// 编译器产生出来的暂时对象
X temp0;
// 编译器对 copy constructor 的调用
temp0.X::X( xx )
// 重新改写函数调用操作,以便使用上述的暂时对象
foo( temp0 ) ;
// 改变函数声明
void foo( X& x0 );

3、返回值初始化(Return Value Initialization)

已知下面这个函数定义:

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

 返回值如何从函数中的局部变量xx拷贝来呢?可能的程序转换,分为以下两步:

  1. 函数增加一个引用参数,用代表返回值;
  2. 在return指令前,增加一个拷贝构造函数,以将局部变量修改引用参数。
// 以反映出 copy constructor 的应用
// C++ 伪码
void
bar( X& _result ) // 译注:加上一个额外参数
{
    X xx;
    // 编译器所产生的 default constructor 调用操作
    xx.X::X();
    // 处理 xx
    // 编译器所产生的 copy constructor 调用操作
    __result.X::XX( xx );
    return;
}

那么,调用处就会被改写成这样:

  1. 函数返回到临时变量(左值)
    // --- 转换前 ---
    X xx = bar();
    // --- 转换后 ---
    X xx;
    bar( xx );
  2. 函数返回右值直接调用
    // --- 转换前 ---
    bar().memfunc();// 译注:执行 bar() 所传回之 x class object 的memfunc()
    // --- 转换后 ---
    X temp0;
    ( bar( temp0 ),temp0 ) .memfunc();
  3. 函数指针
    // --- 转换前 ---
    X ( *pf)();
    pf = bar;
    // --- 转换后 ---
    void ( *pf )( X& );
    pf = bar;

4、在使用者层面优化(Optimization at the user level)

针对这些程序改写的情况,我们做出一些优化,来避免临时对象的构造拷贝与析构。

(1) 在使用者层面做优化(Optimization at the User Level)

程序员需要避免返回函数内局部变量的情况,

// 这导致 xx 被“memberwise”地拷贝到 编译器 产生的临时对象 result 之中。
X bar( const T &y, const T &z ){
    X xx;
    //..·以y和z 来处理 xX
    return xx;
}

为了避免此情况,可以定义另一个 constructor,可以直接计算 xx 的值:

X bar( const T &y, const T &z ){
    return x( y, z );
}

于是 result 被直接计算出来,而不是经由 copy constructor 拷贝而得!

// C++ 伪码
void bar( X & result ) { // 译注:上行是否应为 bar( X& result,const T &y,const T &z )
    result.X::X( y, z );
    return;
}

但这样不好,会导致出现大量的 constructor ,大大增加了代码维护难度。class 的设计是以效率考虑居多,而不是以“支持抽象化”为优先。

5、在编译器层面优化(Optimization at the Compiler level)

return 指令传回相同类型的局部变量(named value),因此编译器有可能自己做优化,也就是大名鼎鼎的NRVO。方法是以 resulf 参数取代 named return value。(针对3.1 的第二点)

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

 编译器把其中的 xx 以 __result 取代:

void bar( X & result ) {
    // default constructor 被调用
    // C++ 伪码
    result.X::X();

    // 直接处理result
    return;
}

6、Copy Constructor: 要还是不要

🍉 什么时候需要?

  1. 对于仅值存储来说,默认的 bitwise copy 效率高,且安全(没有memory leak 和 addres aliasing 的问题);
  2. 如果你的 class 需要大量的 memberwise 初始化操作,例如以传值(by value)的方式传回 objects,那么在允许NRV 的前提下,提供一个 copy constructor 的 explicit inline 函数实体就非常合理。

已知下面的 3D 坐标点类:

class Point3d {
public:
    Point3d( float x, float y, float z );
private:
    float _x,_y, _z;
}

🍇应该如何实现

实现 copy constructor 的最简单方法像这样

Point3d;:Point3d( const Point3d &rhs )
x = rhs._x;
y = rhs._y;
z = rhs._z;

 使用 C++ library 的 memcpy() 会更有效率,但有一定的约束前提。

Point3d::Point3d( const Point3d &rhs ){
    memcpy( this, &rhs, sizeof( point3d ) );
}

不管使用 memcpy 或 memset,都只有在“classes 不含任何由编译器产生的内部 members”时才能有效运行。如果 Point3dclass 声明一个或一个以上 virtual functions,或内含一个 virtual base class,那么使用上述函数将会导致那些“被编译器产生的内部 members”的初值被改写。例如,已知下面声明: 

// 扩张后的 constructor
// C++ 伪码
Shape:: shape () {
    // vptr 必须在使用者的代码执行之前先设定妥当
    _vptr_shape =_vtbl Shape;
    // 喔欧:memset 会将 vptr 清为 0
    memset( this, 0, sizeof( Shape ) );
};

请注意:

  • copy constructor 的应用,迫使编译器多多少少对你的程序代码做部分转化。
  • 尤其是 当一个函数以传值(by value)的方式传回一个 class object,而该 class 有 copy constructor,可能会触发NRVO。

参考

https://zhuanlan.zhihu.com/p/655957750

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值