副标题#e#
***纯虚拟函数***
在设计抽象基类时,需要留意以下几点:
(1)不要将destructor 声明为pure virtual function;
假如将destructor声明为pure virtual function,则设计者一 定得界说它。因为每一个derived class destructor会被编译器加以扩展,以静态挪用得方法挪用其 “每一个virtual base class”以及“上一层base class”的 destructor.
(2)不要将那些函数界说内容并不与范例有关的函数设计为virtual function,因 为其险些不会被后继的derived class改写。
(3)对付其derived class大概修改某一个data member的函数,不该被声明为const.
***“无担任”环境下的工具结构***
先 界说class Point:
class Point {
public:
Point(float x = 0.0, float y = 0.0) : _x(x),_y(y) {}
virtual float z();
protected:
float _x,_y;
};
你可不能小看z()这个virtual function给class Point带来的庞大变革。virtual function的引入促使每一个class Point拥有一个vtpr,这样一来,编译器在constructor中添加了对 vptr举办初始化的代码,而copy constructor和copy assignment operator也会对vptr举办设定,而不 再是原先简朴的bitwise操纵了。
请看以下的代码:
Point foobar()
{
Point local;
Point *heap = new Point;
*heap = local;
delete heap;
return local;
}
将被内部转化为:
Point foobar(Point &_result)
{
Point local;
local.Point::Point();
Point *heap = _new (sizeof(Point));
if(heap != 0)
heap->Point::Point();
*heap = local;
_result.Point::Point(local); // copy constructor的应用
local.Point::~Point();
return;
}
从以上代码的转化可以看出:一般而言,假如你的设计之中有许多函 数都需要以传值方法(by value)传回一个local class object,那么提供一个copy constructor就比 较公道。
***担任体系下的工具结构***
假设class Point3d虚拟担任于class Point,但 由于class Point仅存在一份实体,因而class Point3d的constructor需要留意一个问题。
请看下面的担任干系图:
class Point3d : virtual public Point { ... };
class Vertex : virtual public Point { ... };
class Vertex3d : public Point3d, public Vertex { ... };
#p#副标题#e#
凡是来说,class Point3d和class Vertex的constructor均需挪用Point的 constructor,然而,当Point3d和Vertex同为Vertex3d的subobject时,它们对Point constructor的调 用操纵必然不行以产生,而是交由Vertex3d来完成。
那么如何做到这一点呢?其实只需在 constructor中添加一个参数就可以了。譬喻,class Vertex3d在挪用Point3d和Vertex的constructor之 前,老是会把参数_most_derived设为false,于是就压制了两个constructors中对Point constructor的 挪用操纵。
// 在virtual base class环境下的constructor扩充内容
Vertex3d* Vertex3d::Vertex3d(Vertex3d *this, bool _most_derived, float x, float y, float z)
{
if(_most_derived != false)
this->Point::Point(x,y);
// 在挪用上一层 base classes的constructor之前设定_most_derived为false
this->Point3d::Point3d (false,x,y,z);
this->Vertex::Vertex(false,x,y,z);
// 设定vptr
// 安 插user code
return this;
}
为了节制一个class中有所浸染的函数, 编译器只要简朴地节制住vptr的初始化和设定操纵即可。
vptr初始化操纵应该如那里理惩罚?实际情 况是:应该在base class constructors(详细来说,是所有的virtual base classes和上一层的base classes)挪用操纵之后,可是在措施员供给的码或是“member initialization list中所列的 members初始化操纵”之前。为什么是这样呢?
假如每一个constructor都一直期待到其 base class constructors执行完毕之后才配置其工具的vptr,那么每次它都可以或许挪用正确的virtual function实体。
constructor的执行算法凡是如下:
(1)在derived class constructor 中,所有virtual base classes的constructor会被挪用;
(2)在derived class constructor 中,上一层base class的constructor会被挪用;
(3)上述完成之后,工具的vptr(s)被初始 化,指向相关的virtual table(s);
(4)假如class有member class object,尔后者拥有 constructor,那么它们会以其声明顺序的相反顺序被挪用;(5)用户所界说的代码。
下面是 vptr必需被设定的两种环境:
(1)当一个完整的工具被结构起来时,假如我们声明一个Point对 象,Point constructor必需设定其vptr;
(2)当一个subobject constructor挪用了一个 virtual function(无论是直接挪用或间接挪用)时。
#p#分页标题#e#
在明晰了哪些环境下vptr必需被设定,我 们在声明一个PVertex时,各个vptr不再需要在每一个base class constructor中被设定。办理之道是把 constructor破裂为一个完整的object实体和一个subobject实体。在subobject实体中,vptr的设定可以 忽略。
假如我们差池Point供给一个copy assignment operator,而光是依赖默认的memberwise copy,编译器凡是不会发生出一个实体,除非class不表示出bitwise语意。关于哪些环境class不表示出 bitwise语意,请拜见念书条记(2)。
由于在virtual base class的拷贝操纵将造成subobject 的多重拷贝,而且该问题至今难以办理。因此笔者的发起是:只管不要答允一个virtual base class的 拷贝操纵,甚至发起:不要在任何virtual base class中声明数据。
***解构语意学***
假如class没有界说destructor,那么只有在class内含的member object(或是class本身的base class )拥有destructor的环境下,编译器才会自动合成出一个来。
其解构顺序与建构顺序正好相反。