- C++中初始化有三种情形:
1.显式初始化,如:
A a(tmp);
A a=tmp
A a=A(tmp);
2. 函数形参的初始化
Standard 规定形式相当于:A a = arg;
3. 返回值的初始化
Standard 规定形式相当于:A a = ret;
已知如下函数定义
X bar()
{
X xx;
//处理xx ...
return xx;
}
bar()的返回值如何从局部对象 xx中拷贝过来呢?cfront的做法是进行如下两步骤的转化:
1.加上一个额外参数,类型是class object的一个reference,这个参数将用来放置
“拷贝建构(copy constructed)”而得的返回值
2.在return指令之前安插一个copy constructor调用操作.
//函数转换
//以反映出copy constructor的应用。
//c++伪码
void bar(X & _result)
{
X xx;
//编译器所产生的default constructor调用操作
xx.X::X();
//......处理xx
//编译器所产生的copy constructor调用操作
_result.X::x(xx);
return;
}
cfront编译器必须转换每一个bar()调用操作,以反映其新定义。如
X xx=bar();将转变成
X xx;
bar(xx);
那VC++上的编译器也是这么实现的吗???在VS2008上我写了如下代码以进行验证:
#include <cstdio>
using namespace std;
class A
{
public:
A(){printf("A()\n");}
A(const A &rhs)
{
a=rhs.a;
printf("A(A)\n");
}
private:
int a;
};
A f()
{
A a;
return a;
}
int main()
{
f();
printf("\n");
A a=f();
}
程序输出为
/*
A()
A(A)
A()
A(A)
/*
先看f()的调用。输出"A(A)"说明调用了Copy Constructor,因此有类似_result.A::A(a)的语句被执行了,所以变量a必然被创建了,"A()"就是a被默认构造函数构造出来时输出的。这时候又有个疑问,_result是一个引用,既然它能执行_result.A::A(a),肯定是因为它指向了某个变量,那它指向了哪个变量呢(注:这个变量不可能是a,否则不会调用Copy Constructor)?又既然_result指向了某个变量,那么这个变量必然要在栈中要有内存空间,那么为什么这个变量没有调用默认构造函数?(否则输出应该有两个"A()")。
我的解释是编译器在这种情况下开辟了一块内存大小为sizeof(A)的内存,然后再让引用_result指向这块内存,但没有调用默认构造函数,因为没有必要对这块内存进行默认初始化。不知道解释正不正确,有兴趣深究的可以研究下反汇编代码
而对A a=f(),则转化成了A b;f(b);
- 优化
一是在使用者层面做优化,二是在编译层在做优化。主要讨论NRV。下面的优化过程就是NRV
X bar()
{
X xx;
//.....处理 xx
return xx;
}
//转化为
void bar(X & _result)
{
//default constructor被调用
//C++伪码
_result.X::x();
//......直接处理 _result
return;
}
书上说NRV优化需要一个copy constructor才能激活。对这个我至今无法理解:第一,如果没有copy constructor,而编译器优化需要,那么编译器可以为我们造一个,不管是trival还是nontrival,因此,优化并不需要我们自己
提供explict copy constructor;第二NRV以后,根本就用不着copy constructor,那还造出来干什么?
这条规则可能适用于作者的cfront编译器,而其具体实现细节不详。
作者说:NRV优化如今被视为标准C++编译器的一个义不容辞的优化操作,我认为不正确,在VS2008上运行如下代码
#include <cstdio>
using namespace std;
class A
{
public:
A()
{
printf("A()\n");
}
A(const A &rhs)
{
a=rhs.a;
printf("A(A)!\n");
}
A operator=(const A &rhs)
{
this->a=rhs.a;
printf("A=A\n");
return *this;
}
private:
int a;
};
A f()
{
A a;
return a;
}
int main()
{
A a;
printf("\n");
f();
printf("\n");
a=f();
printf("\n");
A b=f();
printf("\n");
A c(f());
printf("\n");
A d;
d=f();
}
输出:
/*
A()
A()
A(A)!
A()
A(A)!
A=A
A(A)!
A()
A(A)!
A()
A(A)!
A()
A()
A(A)!
A=A
A(A)!
*/
发现并没有做NVR优化(如果有NVR优化,则不会输出A(A))。另外,值得一提的是A b=f()与A b(f())的输出效果相等,效率很高!可见编译器虽然没有做NVR优化(否则不会输出A(A)),但将其A b=f()转化成了A b;f(b);导致operator=并没有调用!
最后两行代码A d;d=f();居然执行了两次默认构造,两次复制构造与一次赋值!!!这也说明了VS2008的编译器中,初始化语句A a=f() 比A a;a=f()速度要快很多!前者进行了优化使之变成了A a(f())。后者并没有优化!后者先对a初始化,调用默认构造;然后执行f(),调用了一次默认构造与拷贝构造;然后再执行operator =函数,生成另一个__result,再执行拷贝构造!效率何其低下!!!