在 构造函数语义学之程序转化语义学(1) 中编译器做了一些优化,有时他还会给你的程序更多的优化:
(1) 在使用者层面做优化
如果程序员顶一个计算用的 constructor:
X bar( const T &y, const T &z )
{
X xx;
// ...以 y 和 z 来处理 xx
return xx;
}
有的编译器开发人员会另外定义一个 constructor:
X bar( const T &y, const T&z )
{
return X( y,z );
}
定义被转化之后,再做如下的优化:
void ( X &__result , const T &y, const T &z) //我觉得侯捷大师说得对哈
{
__result.X::X( y, z );
return;
}
这样就不用__result就可以被直接计算出来而不用经由 copy constructor 拷贝而得。不过这种解决方法也受到了某种批评,怕那些特殊用途的 constructor 可能会大量扩散。在这个层面上,class 的设计是以效率考虑居多,而不是以“支持抽象化”为优先!
(2) 在编译器层面做优化
X bar()
{
X xx;
// ...处理 xx
return xx;
}
编译器把其中的 xx 用 __result取代
void bar( X &__result)
{
// default constructor 被调用
// C++伪码
__result.X::X();
// 直接处理 __result;
return ;
}
这样的编译器优化操作,有时候被称为 Named Return Value 优化!NRV优化如今已被思维标准 C++ 编译器的一个义不容辞的优化操作,但 NRV 优化在每个编译器上的规则可能不一样。
为什么以上1,2被分为 在用户层面( User-level ) 和 在编译器层面( Compiler-level )做优化呢?大家看看哈,在(1)中是在用户代码中另加了一个 constructor ,故属于用户层面。而 (2) 呢,是在将用户的代码在编译的时候进行优化,故被称为编译器层面优化!
大家再来看下面这个例子:
class A class A
{ {
public: public:
A() A():name(0),num(0)
{ {
name = 0;
num = 0; num = 0;
} }
private: private:
string name; string name;
int num; int num;
} }
请大家对比一下上面 class A 的构造函数的不同,并说出哪种代码在执行时会更快?
答案当然是第二种,我相信很多人都知道这一结果,那这是为什么呢?这要从编译器的优化说起了
上面的第一种写法会被编译器转化成如下代码:
A::A()
{
// 调用 string 的 default constructor
name.string::string();
//产生暂时性对象
string temp = string( 0 );
// "memberwise" 地拷贝 name
name.string::operator = ( temp );
temp.string::~string();
num = 0;
}
而第二种写法会被编译器转化成如下代码:
A::A()
{
//调用 string ( int )constructor
name.string::string( 0 );
num = 0;
}
从转化后的代码可看出成员初始化列表的方式明显快的多,所以大家以后遇到以上情况时,尽量写成成员初始化列表的形式以使你的程序跑的更快。不过一下四种情况可由不得你了,你必须用成员初始化列表的形式也就是上面第二种写法,而不能采用第一种写法。
(1). 当初始化一个 reference member 时。
(2). 当初始化一个 const member 时;
(3). 当调用一个 base class 的constructor ,而它拥有一组参数时。
(4). 但调用一个 member class 的 constructor,而它拥有一组参数时。
继续往下看如下代码
class X
{
int i;
int j;
public:
X(int val):j(val),i(j)
{}
}
咋看上去上面的初始化代码没错,先把 val 值赋给 j,再将 j 的值赋给 i。但实际上是这样吗?来看看编译转化后的代码:
X::X(int val)
{
// 调用 int 的 constructor 来初始化 i 和 j
// 伪C++代码
i = j;
j = val;
}
许多人看完这个转化后的吗之后肯定会一目了然,知道上面代码是有问题的了,并没有达到先赋值给 j 然后再赋值给 i 的目的!读者肯定要问为什么 i = j 会在前面呢?那是因为 class X 在定义 date member 时先定义 i,编译器在优化时会在 constructor 中将先声明的 data member 先初始化。故我们那些写是不对的啦!这样 i 会是一个不确定的值。所以下次,该怎么写你的代码了吧?^_^