构造函数语意学----程序转化语意学
来源:互联网 发布:淘宝做代理发货怎么办 编辑:程序博客网 时间:2024/05/20 09:20
显式的初始化操作
已知这样的定义:
X x0;
下面的三个定义,每一个都明显的以x0来初始化其class Object:
void foo_bar(){
X x1(x0);
X x2 = x0;
X x3 = X(x0);
//.....
}
必要的程序转化有两个阶段:
1.重写每一个定义,其中的初始化操作会被剥除(这里所谓的“定义”是指上述x1,x2,x3三行,在严谨的C++用词中,定义是指占用内存的行为);
2.class的copy constructor调用操作会被安插进去。
举个例子:在明确的双阶段转化之后,foo_bar()看起来像这样:
//可能的程序转化
//C++伪代码
void foo_bar()
{
X x1;//定义被重写,初始化操作被剥除
X x2;//定义被重写,初始化操作被剥除
X x3;//定义被重写,初始化操作被剥除
//编译器安插 X copy constructor的调用操作
x1.X::X(x0);
x2.X::X(x0);
x3.X::X(x0);
// ......
}
其中x1.X::X(x0);
就表现对以下的copy constructor的调用。
X::X(const X& xx);
参数的初始化
C++Standard说,把一个class Object当做参数传给一个函数(或是作为一个函数的返回值),相当于以下形式的初始化操作:
X xx = arg;
其中xx代表形式参数(或返回值)而arg代表真正的参数值,因此已知这个函数:
void foo(X x0);
下面这样的调用方式:
X xx;
foo(xx);
将会要求局部实例x0以memberwise的方式将xx当做初值。
在编译器实现技术上,有一种策略是导入所谓的临时性Object,并调用copy constructor将它初始化,然后此临时性Object交给函数。例如将前一段程序代码转换如下:
//编译器产生出来的临时性对象。
X __temp0;
//编译器对copy constructor的调用
__temp0.X::X(xx);
//重新改写函数调用操作,以便使用上述的临时对象
foo(__temp0);
然而这样的转换只做了一半功夫而已。问题出在foo()的声明上。临时性Object先以class X的copy constructor正确的设定了初值。然后再bitwise方式拷贝到x0这个局部实例中。foo()的声明因而也必须被转化,形式参数必须从原先的一个class XObject改变为一个class reference,像这样:
void foo(X& x0);
其中class X声明了一个的destructor,它会在foo()函数完成之后被调用,对付那个临时性的Object。
返回值的初始化
已知下面这个函数定义:
X bar()
{
X xx;
//....
return xx;
}
你可能会问bar()的返回值如何从局部对象xx拷贝过来?Stroustrup在cfront中的解决做法是一个双阶段转化:
1.首先加上一个额外参数,类型是class Object的一个reference。这个参数将用来放置被“拷贝建构”而得的返回值。
2.在return指令之前安插一个copy constructor调用操作,以便将欲传回之Object的内容当做上述新增参数的初值。
真正的返回值是什么?最后一个转化操作会重新改写函数,使它不传回任何值。根据这样的算法,bar()转换如下:
//函数转换
//以反映出copy constructor的应用
void bar(X &__result){
X xx;
//编译器所产生的default constructor调用操作
xx.X::X();
//....处理xx
//编译器所产生的copy constructor调用操作
__result.X::X(xx);
return ;
}
下面三个初始化操作在语意上相等:
X xx0(1024);
X xx1 = X(1024);
X xx2 = (X)1024;
但是在第二行和第三行,语法明显地提供了两个步骤的初始化操作。
1.将一个临时性的Object设以初值1024;
2.将临时性的Object以拷贝构造的方式作为explicit Object 的初值。
换句话说:
xx0是被单一的constructor操作设定初值。
xx0.X::X(1024);
而xx1或xx2却调用了两个constructor,产生一个临时性Object,并针对该临时性Object调用class X的destructor:
X __temp0;
__temp0.X::X(1024);
xx1.X::X(__temp0);
__temp0.X::~X();
named return value(NRV)优化
Copy constructor:要还是不要?
已知下面的3D坐标类:
class Point3D{
public :
Point3D(float x,float y,float z);
//...
private:
float x,y,z;
};
上述class的default copy constructor被视为trivial,它既没有任何member(或base)class Object带有copy constructor,也没有任何virtual base class或virtual function,所以默认情况下,一个Point3D class object的memberwise初始化操作会导致“bitwise copy”。这样效率很高,很安全。bitwise copy既不会导致memory leak也不会产生address aliasing.
如果被问及是否预见class需要大量的memberwise初始化操作,例如以传值(by value)
的方式传回Object,那么提供一个copy constructor的explicit inline函数实例就非常合理。
例如:
Point3D operator+(const Point3D&,const Point3D &);
.......
所有那些函数都能够良好的符合NRV Template
{
Point3D result;
//计算result
return result;
}
实现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,都只有在classes不含任何由编译器产生的内部members时才能有效运行。如果Point3D class声明一个或一个以上的virtual functions或内含一个virtual base class,那么使用上述函数就会导致哪些被编译器产生的内部member的初值被改写。
class Shape{
public :
//改变内部vptr
Shape(){ memset(this,0,sizeof(Shape));}
virtual ~Shape();
//.....
};
编译器为此constructor扩张的内容看起来像这样:
//扩张后的constructor
Shape::Shape(){
//vptr必须在使用者的代码执行之前先设定妥当
__vptr__Shape = __vtbl__Shape;
//memset 会将vptr清为0
memset(this,0,sizeof(Shape));
}
0 0
- 构造函数语意学----程序转化语意学
- 构造函数语意学
- 2.3程序转化语意学
- 2.3 程序转化语意学
- Chap2-构造函数语意学
- 构造函数之语意学
- 构造函数语意学和Data语意学
- 程序转化语意学 Program Transformation Semantics
- 第2章构造函数语意学
- 构造函数语意学----初始化列表
- 构造函数语意学 笔记(一)
- 构造函数语意学 笔记(二)
- 构造函数语意学 笔记(三)
- 构造函数语意学 笔记(四)
- 拷贝构造函数 copy constructor 语意学
- object model-构造函数语意学
- 第二章 构造函数语意学
- C++函数语意学
- 【NPC】3、3SAT规约到顶点覆盖
- PageUtil分页类
- 《黑客与画家》书评---------------怎样成为一名优秀的黑客
- C6678CSL芯片支持库:第八节 芯片驱动DSP程序
- 个人重构总结
- 构造函数语意学----程序转化语意学
- jsp中使用jstl与EL标签创建九九乘法表
- 爬虫程序的学习经历
- SQL注入攻击(ASP)
- AFNetWorking能做什么
- 第12周 项目3-(3)用递归函数求出两个数的最大公约数
- poj1659 - Frogs' Neighborhood (利用Havel-Hakimi定理判断一个序列是否是可图的)
- hduoj1754
- 《网蜂A8实战演练》——10.Linux 网络设备驱动