Inside the C++ Object Model 学习笔记 第五章构造 解构 拷贝语意学

本章主要内容如题目

1,对象的构造

2,对象的解构

3.,拷贝 少量

 

首先书中给出了一个例子

class Abstract_base
{

public:

 virtual ~Abstract_base()=0;//纯虚析构函数
virtual void interface() const=0; //
纯虚函数
virtual const char* mumble() const{return _mumble;}
protected:

 char *_mumble;

 };

这是一个不太对,也就是不太符合我们的“审美标准”的例子。

 

首先,这个例子没有一个明确的constructor,若没有贼它的子类中将会无法确定初值。,若不在constructor中确定初值,则很有可能破坏其封装特性。这个我们得出的结论。

 

其次,它的析构函数是一个纯虚函数,我们可以静态的调用一个纯虚函数,是的。但作为一个析构函数,每个derived  class destructor都会被编译器加以扩展,以静态调用其“ 每一个virtual base class”以及上一层“base class”的destructor,一次 缺少任何一个“base class”的destructor 都会导致链接失败。关键之处在于编译器并不能在链接时找到纯虚的析构函数,然后合成一个必要的函数实体,因此最好不要把虚的析构函数声明成纯虚的。这是得到的第二个结论。

 

最后,虚拟规格中const 的存在虚拟函数最好不要声明成const ,因为你不能保证derived instance 不修改某个data member

还有就是不要随便把函数定义成虚函数,除非有函数调用的需要,函数的定义与类型有关。没上述关系我们都不需要改写函数,为什么还要用虚函数。编译器还要给他配置vptr等东西,很麻烦。我们以前没学过虚函数,也可以活啊,虚函数就是为了更方便一些么,但不要到处用。

有了以上的观点上面的抽象类应该改为下面这种样子:
class Abstract_base
{
public:
virtual ~Absteact_base(); //
不在是纯虚
virtual void interface()=0; //
不在是const
const char * mumble() const{return _mumble;} //
不在是虚函数
protected:
Abstract_base(char *pc=0); //
增加了唯一参数的构造
Char *_mumble;
};

例子ok了。下边开始的是更进一步的说明

一.“无继承”情况下的对象构造

 

Point global;// 全局

 

Point foorbar()

{

Point local;局部

Point *heap = new Point;

*heap = local;

//…stuff…

Delete heap;

Return local;

}

对于global  trival constructor destructor 要么没有定义,要么就是没有被调用,但是在c++中,这个global会被当做“初始化过的数据”来对待。

对于Point *heap = new Point

会被转换为

 Point *heap = _new(sizeof(Point));

这里注意,没有default constructor 作用于new运算符所传回的Point object 身上,这里只是heap指向了一个被申请了Point大小的一块内存。

接下来是一个赋值操作

*heap = local

若果local曾被正确的初始化过,就没什么问题了。但是我们可以看到local没有被正确的初始化。

对于delete heap

_delete(heap);

最后的return 是一个简单的位拷贝操作,没有调用copy constructor

 

下面改写一下Point

Class Point

{

 Public:

Point(float x =0.0, float y =0.0, float z = 0.0):_x(x), _y(y),_z(z){}

Private:

Float _x, _y,_z;

};

对于Point global 初始化现在又default constructor作用于其上,怎么用第六章讲解的很详细。

对于Point *heap = new Point

这里要返回东西了,因为Point不是那个Point

Point *heap = _new(sizeof(Point));

If(heap !=0)

Heap->Point::Point();

然后把heap指针指向 localobject

*heap = local

其他都一样

二、“继承”情况下的对象构造

Class Point

Public

Pointfloat x = 0.0, float y =0.0

:_x(x),_y(y){}

Virtual  float z();

 

Protected:

Float  _x, _y;

};

有了虚函数,就会增加而外的负担,vptr就来了。他的构造函数也会出现内部膨胀

Point::PointPoint *this float xfloat y):_x(x),_y(y)

{

This->_vptr_Point = _vtbl_Point;

构建vptr

This->_x = x;

This->_y = y;

Return  this;

}

合成copy constructor 和一个 copy assigement operator 操作也变成notrival

 

这是程序员给出的PVertex的构造函数:
PVertex::PVertex(float x,float y,float z):_next(0),Vertex3d(x,y,z),Point(x,y)
{
if(spyOn)
cerr<<”within PVertex::PVertex()”<<”size:”<<size()<<endl;
}
具体继承情况 见书上211

他被扩展

//C++伪码
// PVertex
构造函数的扩展结果
PVertex *
PVertex::PVertex(PVertex * this,bool most_derived,float x,float y,float z)
{
//
条件式的调用虚基类的构造函数
if(_most_derived!=false)
this->Point::Point(x,y);
//
无条件的调用上层基类的构造函数
this->Vertex3d::Vertex3d(x,y,z);

//
将相关的vptr初始化
this->_vptr_PVertex=_vtbl_PVertex;
this->_vptr_Point_PVertex=_vtbl_Point_PVertex;

//
原来构造函数中的代码
if(spyOn)
cerr<<”within PVertex::PVertex()”<<”size:”
//
经虚拟机制调用
<<(*this->_vptr_PVertex[3].faddr )(this)<<endl;
//
返回被构造的对象
return this;
}

构造的顺序:

1. 所有的 virtual base class constructors 必须被调用,从左到右,从最深到最浅:

Ø 如果class 被列于member initialization list 中,那么如果有任何明确指定的参数,都应该传递过去。若没有列于list中,而class 有一个default constructor ,也应该调用之。

Ø 此外,class中的每一个virtual base class subobject 的偏移量(offset)必须在执行期可被存取。

Ø 如果class object 是最底层(most-derived)的class,其constructors 可能被调用;某些用以支持这个行为的机制必须被放进来。

2. 所有上一层的base class constructors 必须被调用,以base class 的声明顺序为顺序(与member initialization list 中的顺序没关联):

Ø 如果class 被列于member initialization list 中,那么任何明确指定的参数,都应该传递过去。

Ø base class 没有列于member initialization list中,而它有一个default constructor (或default memberwise copy constructor),那么就调用之。

Ø 如果base class 是多重继承下的第二或后继的base class,那么this 指针必须有所调整。

3. 如果class object virtual table pointers),它(们)必须被设定初值,指向适当的virtual tables)。

4. 记录在member initialization list 中的data members 初始化操作会被放进constructor 的函数本身,并以members 的声明顺序为顺序。

5. 如果有一个member 并没有出现在member initialization list 之中,但它有一个default constructor,那么该default constructor 必须被调用。

请注意,虽然一个classdestructor virtual 的,但是如果它被包含在另一个class中,那么它的调用操作会被静态决议出来。

通过上面的代码我们可以比较清晰的了解在有多重继承+虚拟继承的时候构造一个对象时,编译会将构造函数扩充成一个什么样子。以及扩充的顺序。知道了这个相对于无继承,或者不是虚拟继承时对象的构造应该也可以理解了。与构造对象相对应的是析构。但是构造函数和析构函数和newdelete不同,他们并非必须成对的出现。决定是否为一个类写构造函数或者析构函数,是取决于这个类对象的生命在哪里结束(或开始)。需要什么操作才能保证对象的完整。象构造函数一样析构函数的最佳实现策略是维护两份destructor实体。一个complete object实体,总是设定好vptrs,并调用虚拟基类的析构函数。一个base class subobject实体。除非在析构函数中调用一个虚函数,否则绝不会调用虚拟基类的析构函数,并设定vptrs

对于vptr

Vptr 初始化语意学:

C++ 语言规定:在一个classbase class constructor(和destructor 中,经由构造中的对象(derived class)来调用一个virtual function,其函数实体应该是在此classbase class 中有作用的那个。也就是都静态决议,不用到虚拟机制。也就是说,虚拟机制本身必须知道是否这个调用源自一个constructor 之中。而根本的解决之道是,在执行一个constructor 时,必须限制一组virtual functions 候选名单。答案是通过vptr。而vptr 的适当初始化时间是在base class constructors 调用操作之后但是在程序员供应的码或是“member initialization list 中所列的members初始化操作”之前。因此在class constructor member initialization list 中调用该class 的一个虚拟函数是安全的,但是在一个class member initialization list 中供应参数一个base class constructor 时调用虚拟函数就是不安全的。

 

三 、解构语意学

如过class 没有定义destructor,那么只有在class 内带的member object(或是class自己的base class)拥有destructor 的情况下,编译器才会自动合成出一个来。

Destructor 被调用的顺序:

1. destructor 的函数本身首先被执行。

2. 如果class 用哟member class objects,而后者拥有destructors,那么它们会以其声明顺序的相反顺序被调用。

3. 如果object 内带一个vptr,则现在被重新设定,指向适当之base class virtual table

4. 如果有任何直接的(上一层)nonvirtual base classes 拥有destructor,它们会以声明顺序的相反顺序被调用。

5. 如果有任何virtual base classes 拥有destructor,而当前讨论的这个class 是最尾端(most-derived)的class,那么它们会以其原来的构造顺序的相反顺序被调用。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值