一、纯虚函数的存在
可以定义和调用一个pure virtual function, 不过只能被静态调用(invoked statitcally),不能经由虚拟机制调用
Abstract_base::interface();
如上述,Abstract_base是一个虚基类。这只取决于类设计者要不要这么做。
但是对于pure virtual destrcutor, 类设计者一定要定义它,因为每个drived class destructor会被编译器加以扩张,然后以静态方式进行调用"每一个virtual base class”以及“上一层base class”的destructor。
所以一般比较好的处理方式,不要把virtual destructor声明为pure。
二、“无继承”情况下的对象构造
有三种对象产生的方式:global内存控制,local内存配置和heap内存配置。
两种初始化方式
Point local1 = {1.0, 1.0, 1.0};
Point local2;
local2._x = 1.0;
local2._y = 1.0;
local2._z = 1.0;
local1对应是explicit initialization list,这比local2声明有效的多。因为当函数的activation record放进程序堆栈时,上述的initialization list中的常量就被放进local内存中了。 并且,explicit initialization list只有当类数据成员声明为public时才有效。
三、为继承做准备
当一个类声明多了virtual function,也就是可以进行操作的动态决议时,会有如下变化:
- 没个class object多负担一个vptr
- 所定义的contructor将发生膨胀,被附加一些代码以便将vptr进行初始化
- 合成一个copy constructor和copy assignment operator,主要是由于vptr的存在,bitwise copy可能会发生问题。
Point foobar()
{
Point local;
// ...
return local;
}
会被转化为如下:
Point foobar( Point &_result )
{
Point local;
// ...stuff...
_result.Point::Point(local); //调用copy constructor
local.Point::~Point();
return;
}
如果设计的类中,函数需要以传值方式(by value)传回一个local class object,最好提供一个copy construtor。
四、继承体系下的对象构造
T object;
编译器会扩充每一个constructor,扩充程度视class T的继承体系而定。大致扩充如下:
- 记录在member initialization list中的data members初始化操作会被放进constructor的函数本体,并以members的声明顺序为顺序。
- 如果member没有出现在initialization list中,但有一个default constructor,那么default constructor必须背调用。
- 如果class object有vptr,则必须设定初值指向适当的virtual table。
- 所有的上层base class constructors必须被调用,以base class的声明顺序为顺序。
- 如果base class被列于member initialization list中,那么任何显示指定的参数都需要被传递过去
- 如果没有列于上述,则调用defualt constructor或default memberwise copy constructor。
- 如果base class是多重继承下的第二或后继的base class,那么this指针需要有所调整
- 所有的virtual base class constructor必须被调用,从左至右,从深到浅。
- 1. 如4(1)
- class的每一个virtual base class subobject的偏移位置必须在执行期可被存取。
- 支持最底层的constructor的某种调用机制需要放进来。
如果Point destructor是inline函数,每一个调用操作在调用地点会被扩展开来。
当
Line b = a;
被写出来时,implicit Line copy constructor会被合成出来,成为一个inline public member
当
a = b;
被写出来时,implicit copy assignment
operator 会被合成出来,成为一个inline public member。
五、虚拟继承
对于虚拟继承,越是往后的derive class,其相应就需要完成对共享基类对象的构造。这是顶端派生类所干的事。同时,在扩展中还需要将标记,使得基类对象只被实例化一份。而后继的类不需要再实例化共享基类。
只有当一个完整的class object被定义出来,virtual base class constructor才能被调用,如果object只是某个完整的object的suboject,它就不会被调用。
一般而言,一个class的constructor中,经由构造中的对象来调用一个virtual function,其函数实例化应该是此class中有作用的那一个。
constructor的调用顺序,从根源到末端, 由内向外,也就是从基类到派生类方向。
在构造过程中,对于每次调用,该如何决议 ==> 需要对每个调用操作以静态方式决议,不要用到虚拟机制。也就是类似
Point3d::size();
同时,如果size中需要调用其他函数实例,这个时候急需要经由virtual机制决定其动态。
那到底什么时候是以静态方式调用,什么时候以虚拟机制调用?
根本解决之道,在执行一个constructor时,必须限制一组virtual function候选名单。换一句话说,就是为了控制一个class中的有所作用的函数,编译系统需要控制住vptr的初始化和设定操作。
vptr的初始化操作应该如何处理?
每一个constructor都一直等待到其base class constructor 执行完毕之后才设定其对象的vptr,那么每次就能够调用到正确的virtual function实例。
constructor的执行算法通常如下:
- 在derived class constructor中,所有的“virtual base classes”及“上一层base class”的constructors会被调用。
- 上述完成之后,对象的vptr(s)被初始化,指向相关的virtual table(s);
- 如果有member initialization list的话,将在constructor体内扩展出来。这必须在vptr设定之后才这么做,以确保有一个virtual member function被调用。
- 执行程序猿的代码。
六、对象复制语意学
只有在默认行为所导致的语意不安全或不正确时,我们才需要设计一个copy assignment operator。
如果不对类提供拷贝复制符,那么编译就和copy constructor的情形一样,不会产生实例。
什么时候不会表现出bitwise copy?
- 当一个class内含一个member object,而其class有一个copy assignment operator
- 当一个class的base class有一个copy assignment operator
- 当一个class声明了任何virtual function(因为不能拷贝右端的vptr)
- 当一个class继承自一个virtual base class。
copy assignment 缺乏member assignment list,也就是如下性质是不支持的:
inline Point3d&
Point3d::operator=( const Point3d &p3d ) : Point( p3d ), z ( p3d._z ) {}
一种方法可以保证most derived class完成virtual base class subobject的copy行为,也就是在derived class 的 copy assignment operator函数实例最后,显示调用operator。如:
inline Vertex3d &
Vertex3d::operator=( const Vertex3d &v )
{
this->Point3d::operator=(v);
this->Vertex::operator=(v);
this->Point::operator=(v);
}
七、析构语意学
如果class没有定义destructor,那么只有在class内含的member object(抑或是class自己的base class)拥有destructor的情况下, 编译器才会自动合成一个出来。否在destructor被视为不需要。也就不需要合成。
一个destructor被扩展的方式类似constructor,但顺序相反。
- 如果object内含一个vptr,那么首先重设相关的virtual table 3
- destructor的函数本体现在被执行,也就是说vptr会在程序员的代码执行前被重设 1
- 如果class拥有member class objects,而后者拥有destructor,那么它们会以其声明的相反顺序被调用 2
- 如果有任何直接的nonvirtual base classes 拥有 destructor,它们会以其声明的顺序的相反顺序被调用 4
- 如果有任何virtual base classes拥有destructor,而目前这讨论的这个class是最尾端(most derived)的class,那么她们会以其相反的构造顺序的相反顺序被调用。 5
参考:
侯捷 深度探索C++对象模型