当前位置:编程学习 > C/C++ >>

由一道题目想到的C++编译器优化问题

这两天看到了一个问题,看似简单,但是用的知识着实不少,原题如下:


[cpp]
#include "stdafx.h"  
 
class Base 

public: 
    Base(){} 
    virtual ~Base(){} 
    Base(const Base &other);            // 只声明, 没定义  
private: 
    Base &operator=(const Base &other); 
} ; 
 
int _tmain(int argc, _TCHAR* argv[]) 

    const Base &b = Base() ;        // 为什么没有导致链接错误? 应该调用拷贝构造函数才对, 然而我只声明没定义!     
 
    return 0; 

#include "stdafx.h"

class Base
{
public:
    Base(){}
    virtual ~Base(){}
    Base(const Base &other);            // 只声明, 没定义
private:
    Base &operator=(const Base &other);
} ;

int _tmain(int argc, _TCHAR* argv[])
{
    const Base &b = Base() ;        // 为什么没有导致链接错误? 应该调用拷贝构造函数才对, 然而我只声明没定义!  

    return 0;
}

我一开始想到的就是编译器优化了:推测下编译器应该做了优化: const Base &b = Base() ;这么写的话,按照语义是:

1.调用Base的构造函数

2.调用Base的赋值函数 b=临时对象但是编译器大概认为没必要这么两步走,直接调用了Base的构造函数。。。。

后面经过大侠A的指导,指出问题所在:

Base b = a 的时候, b 还没有构造, 所以需要先对 b 进行构造, 再紧接着进行赋值, 你说 c++ 编译器的设计者, 能不优化一下么? 所以在所有 c++ 规定了这个行为, 即 Base b = a 就是Base
 b(a), 这也是 c++ 语法的 sweet.


恩。。。。那么暂时我们就认为const Base &b = Base() ; 被编译器变成了Base b(Base()),那么还是应该会调用默认构造函数和拷贝构造函数啊,可以明明程序并没有报错!!!

关键人物大侠B出现了:


[cpp]
个人以为,const Base &b = Base() ; 引用是直接绑定在右边的那个匿名对象上的,所以木有发生拷贝构造,所以跟拷贝构造函数是否定义木有关系。 
参考 ISO/IEC 14882:2003(E)   
8.5.3 References 
第4 5条款 
— Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).  
个人以为,const Base &b = Base() ; 引用是直接绑定在右边的那个匿名对象上的,所以木有发生拷贝构造,所以跟拷贝构造函数是否定义木有关系。
参考 ISO/IEC 14882:2003(E) 
8.5.3 References
第4 5条款
— Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const). [cpp]
[Example: 
double& rd2 = 2.0; // error: not an lvalue and reference not const  
int i = 2; 
double& rd3 = i; // error: type mismatch and reference not const  
—end example] 
— If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined): 
— The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object. 
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.93) 
[Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const
int i = 2;
double& rd3 = i; // error: type mismatch and reference not const
—end example]
— If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined):
— The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.93)
看着这么一大堆英文又迷糊了。。。。静下心来好好读了下:

关键在于这段话


[cpp]
— If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined)://如果初始化表达的右边是个类类型,并且左边是一个对右值的常量引用,那么这种情况呢,可以由编译器用以下两种方式实现(两者选一):  
— The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.//直接将引用绑定到右值  
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.//临时对象被创建,并且调用拷贝构造函数将临时对象拷贝到右值,再将引用绑定到临时对象。 
— If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined)://如果初始化表达的右边是个类类型,并且左边是一个对右值的常量引用,那么这种情况呢,可以由编译器用以下两种方式实现(两者选一):
— The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.//直接将引用绑定到右值
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.//临时对象被创建,并且调用拷贝构造函数将临时对象拷贝到右值,再将引用绑定到临时对象。
显然我测试到的VC、GCC都选择了第一种做法直接将引用绑定到右值。。。。~

关于引用的几点说明,摘自于网络:http://www.zzzyk.com/kf/201203/121502.html


[cpp]
/*
(1)&在此不是求地址运算,而是起标识作用。
 
(2)类型标识符是指目标变量的类型。
 
(3)声明引用时,必须同时对其进行初始化。
 
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
 
ra=1; 等价于 a=1;
 
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,
它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
 
(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
*/ 
/*
(1)&在此不是求地址运算,而是起标识作用。

(2)类型标识符是指目标变量的类型。

(3)声明引用时,必须同时对其进行初始化。

(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。

ra=1; 等价于 a=1;

(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,
它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
故:对引用求地址,就是对目标变量求地址。&ra与&a相等。

(6)不

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,