2、 可以定义并调用一个pure virtualfunction,但它只能被静态调用,不能经由虚拟机制调用。
3、 class设计者一定得定义pure virtual destructor,因为每一个derived class destructor会被编译器加以扩张,静态调用每一个virtualbase class以及上一层base class的destructor。只要缺乏任何一个base class destructors的定义,就会导致链接失败。
“无继承”情况下的对象构造
在“无继承”情况下,观念上,编译器会为class声明一个trivial default constructor、一个trivial destructor、一个trivial copy constructor,以及一个trivial copy assignment operator。然而,事实上trivial members要不是没被定义就是没被调用。
抽象数据类型
如果要将class中的所有成员都设定为常量初值,那么给予一个explicit initialization list会比较有效率些(比起意义相同的constructor的inline expansion而言)。甚至local scope中也是如此,如下例:
void mumble() { Point local1={1.0,1.0,1.0}; Point local2; //相当于一个inline expansion //explicit initialization list会稍微快些 local2._x=1.0; local2._y=1.0; local2._z=1.0; }
local1的初始化操作会比local2的有效率些。
Explicit initialization list有三项缺点:
只有当class members都是public时,才有效;
只能指定常量,因为它们在编译时期就可以被评估求值;
由于编译器并没有自动施行之,所以初始化行为的失败可能会比较高一些;
为继承做准备
具有多态性质相关的类,将为“继承性质”以及某些操作的动态决议做准备。virtual functions的导入促使每一个class object有用一个virtual table pointer。这个指针给我们提供了virtual接口的弹性,其成本是:每一个object需要额外的一个word空间。除了每一个class object多负担一个vptr之外,virtual function的导入引发编译器对class产生膨胀作用:
我们所定义的constructor被附加一些代码,以便初始化vptr;
合成一个copy constructor和一个copy assignment opertor,而且其操作不再是trivial(但implicit destructor仍是trivial);
继承体系下的对象构造
一般而言,继承体系下编译器对constructor所做的扩充操作以及次序大约如下:
记录在member initialization list中的data members初始化操作会被放进constructor的函数本体,并以members的声明顺序为顺序;
如果一个member并没有出现在member initialization list之中,但它有一个default constructor,那么该default constructor必须被调用;
在那之前,如果class object有virtual table pointer(s),它(们)必须被设定初值,指向适当的virtual table(s);
在那之前,所有上一层的base class constructors必须被调用,以base class的声明顺序为顺序(与member inlitialization list顺序没有关联)
如果base class被列于member initialization list中,那么任何显式指定的参数都应该传递过去;
如果base class没有被列于member initialization list中,而它有default constructor,那么就调用之;
如果base class是多重继承下的第二或后继的base class,那么this指针必须有所调整。
在那之前,所有virtual base class constructors必须被调用,从左至右,从最深至最浅;
如果class被列于member initialization list中,那么如果有任何显式指定的参数,都应该传递过去。如果没有列于list之中,而class有一个default constructor,亦应该调用之;
此外,class中的每一个virtual base class subobject的偏置位置,必须在执行期可被存取;
如果class object是最底层的class,其constructors可能被调用;某些用以支持这一行为的机制必须被放进来。
虚拟继承
考虑下面三种类的派生情况:
class Vertex : virtual public Point{...}; class Vertex3d : public Point3d,public Vertex{...}; class PVertex : public Vertex3d{...};

Vertex的constructor必须也调用Point的constructor。然而,当Point3d和Vertex同为Vertex3d的subobjects时,他们对Point constructor的调用操作一定不可以发生;取而代之的是,作为一个底层的class,Vertex3d有责任将Point初始化。而更往后(往下)的继承,则由PVertex来赋值完成“被共享之Point subobject”。
因此,编译器在虚拟继承中需要对constructor采用不同与普通继承的扩充。
vptr初始化语意学
vptr初始化操作应该如何处理?在base class constructors调用操作后,但是在程序员供应的代码或是“member initialization list中所列的members初始化操作”之前。
constructor执行算法过程:
在derived class constructor中,“所有virtual table classes”及“上一次base class”的constructors会被调用;
上述完成后,对象的vptr(s)被初始化,指向相关的virtual table(s);
如果由member initialization list的话,将在constructor体内展开来。这必须在vptr被设定之后才做,以免有一个virtual member function被调用;
最后,执行程序员所提供的代码。
对象复制语意学
显式拒绝把一个class object指定给另一个class object的做法:将copy assignment operator声明为private,并且不提供其定义。把它设为private,就不允许任何地点(除了member functions及该class的friends之中)做赋值操作。不提供其函数定义,则一旦某个member function或friend企图影响一份拷贝,程序在链接时就会失败。
只有在默认行为所导致的语意不安全或者不正确时,才需要设计一个copy assignment operator。
一个class对于默认的copy assignment operator,在以下情况不会表现出bitwise copy语意(不表现出bitwise copy语意,则编译器生成default copy assignment operator):
当class内含一个members object,而其class有一个copy assignment operator时
当一个class的base class有一个copy assignment operator
当一个class声明了任何virtual functions时
当class继承一个virtual base class时
对于Point class,这样的赋值操作:
Point a,b; …… a=b;
由bitwise copy完成,把Point b拷贝到Point a,期间并没有copy assignment operator被调用。
析构语意学
如果class没有定义destructor,那么只有在其内含的member object或base class拥有destructor时,编译器才会自动合成出一个destructor。一个由程序员定义的destructor被扩展的方式类似constructors被扩展的方式,只是顺序相反:
destructor的函数本体首先被执行;
如果class拥有member class objects,而后者拥有destructors,那么它们将以声明的相反顺序而调用;
如果object内含一个vptr,则现在被重新设定以指向适当base class之virtual table;
如果有任何直接的nonvirtual base classes拥有destructor,它们将以声明的相反顺序而调用;
如果有任何virtual base classes拥有destructor,而前面讨论的这个class是most-derived class,那么它们会以原先构造顺序的相反顺序被调用。
284




被折叠的 条评论
为什么被折叠?



