见书上P211。
这是程序员给出的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;
}
它可能被扩展成为:
//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;
}
通过上面的代码我们可以比较清晰的了解在有多重继承+虚拟继承的时候构造一个对象时,编译会将构造函
数扩充成一个什么样子。以及扩充的顺序。知道了这个相对于无继承,或者不是虚拟继承时对象的构造应该
也可以理解了。与构造对象相对应的是析构。但是构造函数和析构函数和new和delete不同,他们并非必须
成对的出现。决定是否为一个类写构造函数或者析构函数,是取决于这个类对象的生命在哪里结束(或开
始)。需要什么操作才能保证对象的完整。象构造函数一样析构函数的最佳实现策略是维护两份destructor
实体。一个complete object实体,总是设定好vptrs,并调用虚拟基类的析构函数。一个base class
subobject实体。除非在析构函数中调用一个虚函数,否则绝不会调用虚拟基类的析构函数,并设定vptrs。
一个对象生命结束于析构函数开始执行的时候。它的扩展形式和构造函数的扩展顺序相反。
当编译一个C++程序时,计算机的内存被分成了4个区域,一个包括程序的代码,一个包括所有的全局变
量,一个是堆栈,还有一个是堆(heap),我们称堆是自由的内存区域,我们可以通过new和delete把对象放
在这个区域。你可以在任何地方分配和释放自由存储区。但是要注意因为分配在堆中的对象没有作用域的限
制,因此一旦new了它,必须delete它,否则程序将崩溃,这便是内存泄漏。(C#已经通过内存托管解决了
这一令人头疼的问题)。C++通过new来分配内存,new的参数是一个表达式,该表达式返回需要分配的内
存字节数,这是我以前掌握的关于new的知识,下面看看通过这本书,使我们能够更进一步的了解到些什
么。
深度探索深度探索C++对象模型(对象模型(9))
这一章主要是说Runtime Semantics执行期语义学。
这是我们平时写的程序片段:
Matrix identity; //一个全局对象
Main()
{
Matrix m1=identity;
……
return 0;
}
很常见的一个代码片段,雷神从来没有考虑过identity如何被构造,或者如何被销毁。因为它肯定在Matrix
m1=identity之前就被构造出来了,并且在main函数结束前被销毁了。我们不用考虑这些问题,好象C++就
应该这样。但这本书是研究C++底层机制的。既然我们在看这本书,说明我们希望了解C++的编译器又做了
那些大量的工作,使得我们可以这样使用对象。
在C++程序中所有的全局对象都被放在data segment中,如果明确赋值,则对象以该值为初值,否则所配置