15、无继承情况的对象构造

目录

1、抽象数据类型(Abstract Data Type)

2、为继承做准备


考虑下面这个程序的片段:

(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的初始化操作:

  1. 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值