目录
考虑下面这个程序的片段:
(1) Point global;
(2)
(3) Point foobar()
(4) {
(5) Point local;
(6) Point *heap = new Point;
(7) *heap = local;
(8) // ... stuff ...
(9) delete heap;
(10) return local;
(11)}
1、L5、L6表现出三种不同的对象产生方式:global内存配置、local内存配置和heap内存配置。L7把一个class object指定给另一个,L10返回设定值,L9显式以delete运算符删除heap object。
一个object的生命,是该object的一个执行期属性。local object的生命从L5的定义开始,到L10为止。global object的生命和整个程序的生命相同。heap object的生命从它被new运算符配置出来开始,到它被delete运算符摧毁为止。
下面是Point的第一次声明,可以写成C程序。C++ Standard说这是一种所谓的Plain O1’ Data声明形式:
typedef struct
{
float x, y, z;
}Point;
如果我们用C++编译这段代码,编译器会分析这个声明,并给它贴上Plain O1’ Data标签。当编译器遇到这样的定义:
(1) Point global;
观念上Point的trivial constructor和destructor都会被产生并被调用,constrcutor在程序起始(startup)处被调用而destructor在程序的exit()处被调用(exit()是系统产生,放在main()结束之前)。然而,事实上那些trivial members要不是没被定义,就是没被调用,程序的行为一如它在C中的表现一样。
foobar()函数中的L5,有一个Point object local,同样也是既没有被构造也没有被析构。当然,Point object local如果没有经过初始化,可能会成为一个潜在的程序“BUG”----万一第一次使用它就需要其初值的话(像L7)。至于heap object在L6的初始化操作:
- Point *heap = new Point;
会被转换成对new运算符(由libraries提供)的调用:
Point *heap = __new( sizeof( Point ) );
如果local曾被适当地初始化过,一切就没有问题:
(7)*heap = local;
事实上这一行产生编译警告如下:
warning,line 7:local is used before being initialized.
观念上,这样的指定操作会触发trivial copy assignment operator做拷贝搬运工作。然而实际上该object是一个Plain O1’ Data,所以赋值操作(assignment)将只是像C那样纯粹搬移操作。L9执行一个delete操作:
(9)delete heap;
会被转换成对delete运算符(有libraries提供)的调用:
__delete( heap );
1、抽象数据类型(Abstract Data Type)
以下是Point的声明,在public接口之下多个private数据,提供完整的封装性,但是没有提供任何的virtual function:
class Point
{
public:
Point(float x=0,float y=0,float z=0):_x(x),_y(y),_z(z){}
//no copy constructor,copy operator
//or destructor defined ...
private:
float _x,_y,_z;
};
这个经过封装的Point class,其大小并没有改变,还是三个连续的float。是的,不论是private或public存取层,或是member function的声明,都不会占用额外的对象空间。
2、为继承做准备
我们的第三个Point声明,将为“继承性质”以及某些操作的动态决议(dynamic resolution)做准备。目前我们限制对z成员做存取操作:
class Point
{
public:
Point(float x=0,float y=0)
:_x(x),_y(y){}
//no destructor,copy constructor,or
//copy operator defined ...
virtual float z();
private:
float _x,_y;
};
每一个class object多负担一个vptr之外,virtual function的导入也引发编译器对于我们的Point class产生膨胀作用:
我们所定义的constructor被附加了一些代码,以便将vptr初始化。这些代码必须被附加在任何base class constructors的调用之后,但必须在任何由使用者(程序员)供应的代码之前。例如,下面就是可能的附加结果(以Point为例):
//C++伪代码:内部膨胀
Point *
Point::Point(Point *this,
float x, float y)
:_x(x), _y(y)
{
//设定object的virtual table pointer(vptr)
this->__vptr_Point = __vtbl__Point;
//扩展member initialization list
this->_x = x;
this->_y = y;
//传回this对象
return this;
}
合成一个copy constructor和一个copy assignment operator,而且其操作不再是trivial。如果一个Point object被初始化或以一个derived class object赋值,那么以位为基础(bitwise)的操作可能对vptr带来非法的设定。
//C++伪代码
//copy constructor的内部合成
Point::Point(Point *this, const Point &rhs)
{
//设定object的virtual table pointer(vptr)
this->__vptr_Point = __vtbl__Point;
//将rhs坐标中的连续位拷贝到this对象
//或是经由member assignment 提供的一个member ...
//传回this对象
return this;
}
编译器在优化状态下可能会把object的连续内容拷贝到另外一个object身上,而不会实现一个精确的“以成员为基础memberwise的赋值操作”。C++Standard要求尽量延迟nontrivial members的实际合成操作,直到真正遇到其使用的场合为止。
举个例子,我们把foobar()再次列在此:
(1) Point global;
(2)
(3) Point foobar()
(4) {
(5) Point local;
(6) Point *heap = new Point;
(7) *heap = local;
(8) //...stuff...
(9) delete heap;
(10) return local;
(11) }
L1的global初始化操作、L6的heap初始化操作以及L9的heap删除操作,都还是和稍早的Point版本相同,然而L7的memberwise赋值操作:
*heap = local;
很可能触发copy assignment operator的合成,及其调用操作的一个inline expansion(行内扩张);以this取代heap,而以rhs取代local。
L10,由于拷贝构造函数的出现,很可能转换成下面的样子。
//C++伪代码:foobar()的转换,
//用以支持copy constructor
Point foobar(Point &__result)
{
Point local;
local.Point::Point(0.0, 0.0);
//heap的部分与前相同
//copy cnstructor 的应用
__result.Point::Point(local);
//local对象的destructor将在这里执行。
//调用Point定义的destructor
//local.Point::~Point();
return;
}