目录
1.显示的初始化操作
已知有这样的定义:
X x0;
下面的三个定义,每一个都明显地以x0来初始化其class object;
void foo_bar()
{
X x1(x0); //译注:定义了x1
X x2 = x0; //译注:定义了x2
X x3 = X(x0);//译注:定义了x3
//...
}
必要的程序转化有两个阶段:
1)在C++中,“定义”是指“占用内存”的行为。上面的代码将会重写每一个定义,其中的初始化操作会被剥离。
2)Class 的copy constructor调用操作将会被安插进去。
举个例子,在明确上面的两种转换后,foo_bar()可能看起来像这样子:
//可能的程序转换
//C++伪代码
void foo_bar(){
X x1;//译注:定义被重写,初始化操作被剥离
X x2;//译注:定义被重写,初始化操作被剥离
X x3;//译注:定义被重写,初始化操作被剥离
//编译器安插X copy construction的调用操作
x1.X::X(x0);
x2.X::X(x0);
x3.X::X(x0);
//...
}
2.参数的初始化
把一个class object当作参数传给一个函数(或是作为一个函数的返回值),如下的代码要求局部的实例x0以memberwise的方式将xx当作初值。在编译器的实现技术上,有一种策略是导入临时性object,并调用copy constructor将它初始化,然后将此临时性object交给函数。
void foo(X x0);
X xx;
//...
foo(xx);
例如将前一段程序代码转换如下:
//C++伪代码
//编译器产生的临时对象
X __temp0;
//编译器对copy constructor的调用
__temp0.X::X(xx);
//重新改写函数调用操作,使用上述的临时对象
foo(__temp0);
然而这样做有个问题,问题在foo的声明上。foo()的声明必须被转换,形式参数必须从原来的一个class X object改变成一个class X reference,像这样子:
void foo(X &x0);
其中class X声明了一个destructor,它会在foo()函数完成之后被调用,对付那个临时性的object。
3.返回值的初始化
已知下面的这个函数定义:
X bar()
{
X xx;
// 处理 xx ...
return xx;
}
你可能会问bar()的返回值如何从局部对象xx中拷贝过来?Stroustrup在cfont中的解决办法是一个双阶段转化:
首先加上一个额外参数,类型是class object的一个reference。这个参数用来放置被“copy constructed”而得的返回值。
然后在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;
}
在举几个例子,例如: X xx = bar();将被转换成如下的两个指令语句。
X xx;
bar(xx);
如果程序声明了一个函数指针,像这样子:
X (*pf)();
pf = bar;
它也被转换成这样子。
void (*pf)(X &x);
pf = bar;
4.在使用者层面做优化
最先提出“程序员优化”的概念是Jonathan Shopiro,他定义了一个“计算用”的constructor。换句话说,程序员不再写:
X bar(const T &y,const T &z)
{
X xx;
//...以y和z来处理xx
return xx;
}
那会要求xx被“memberwise”的拷贝到编译器产生的__result之中。Jonathan定义另外一个constructor,可以直接计算xx的值。
X bar(const T &y,const T &z)
{
return X(y,z);
}
于是当bar()的定义被转换之后,效率会更加的高。
//C++伪代码
void
bar(X &__result,const T &y,const T &z)
{
__result.X::X(y,z);
return;
}
__result被直接计算出来,而不是经过copy constructor拷贝得到的。
5.Copy Constructor要不要?
已知下面的3D坐标点类:
class Point3d
{
public:
Point3d(float x,float y,float z);
//...
private:
float _x,_y,_z;
};
这个class的设计者需要提供一个explicit copy constructor吗?答案是no,三个坐标成员是以数值来存储的。bitwise copy既不会导致memory leak,也不会产address aliasing。它是最快速安全的。
实现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(),都只有在“class不含有任何由编译器产生的内部members”时才能有效的运行。如果Point3d class声明一个或一个以上的virtual functions,或内含一个virtual base class,那么使用上述的函数将会导致那些“被编译器产生的内部members”的初值改写。
如你所见,如果要正确使用memcpy()和memset(),则需要掌握某些C++ Object Model的语意学知识。