5.程序转换语意学

目录

1.显示的初始化操作

2.参数的初始化

3.返回值的初始化

4.在使用者层面做优化

5.Copy Constructor要不要?


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的语意学知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值