构造,析构,拷贝语义学 semantics of construction destruction and copy
纯虚拟函数的存在(Presence of a Pure Virtual Function)
纯虚拟函数是可以定义并调用的,只不过只能被静态调用,不能经由虚拟机制。(经过试验vs2008是不可以的)
而pure virtual destructor :class 设计者一定要定义它。因为每一个derived class的destructor会被编译器加以扩展,以静态方式调用其每一个virtual base class 以及上层base class 的destructor ,因此缺乏任何一个base class destructor 的定义就会导致连接失败
基类的析构函数应该设置为虚拟的,不一定是纯虚的 ,除非在希望将一个类变成抽象类(不能实例化的类),而这个类又没有合适的函数可以被纯虚化的时候,可以使用纯虚的析构函数来达到目的。
class A{ public: char* pChar;A(){pChar=new char[20];};~A(){delete []pChar;}};
class B:public A{ public: char* pChar;B(){pChar=new char[20];}~B(){delete []pChar;}};
int main(){ A* a = new B; delete a;}
如上,如果A的析构函数没有设置为虚拟的,那么delete a 的时候只有调用了A的析构函数,真正的B并没有调用。而如果虚拟的话,那么a调用析构函数会调用B的析构函数,而B的析构函数会调用A的析构函数。
虚拟函数的取舍:如果一个函数被设计为虚拟函数,但是他函数定义并不与类型相关,也就是说继承类改写他的几率很低,那么这是个糟糕的事情。如果不是虚拟而且能内联,这是个很大的损失
---------
没有继承下的对象构造
Plain OI’s data 形式
例如 struct {float x,y,z;}
理论上编译器会生成没有用的trival ctor dtor copytor copy operator ,然而事实上什么也没有发生。
全局对象,c中会被放在bss段,而c++中的全局对象会被视为完全定义,被当做初始化过的数据来对待。
抽象数据类型:
class Point {public: Point (float x=0):_x(x){}; private: _x;}
经过封装的Point class 大小没有改变 由于默认的member wise 赋值语义已经足够,我们无需提供copy constructor 和copy operator。对于global 实体,Point::Point(0) 会自动调用 (程序开始时候startup())
如果要对class所有成员都设定常量初值,那么给予一个explicit initial list 会比较高效。
例如 void mumble(){
Point A1={1};
Point A2; A2._x=1;
}
A2的初始化会比A1慢,因为,当activation record(活动记录 是不是栈中的pc 指针),上述的list中常量就可以放进A1的内存中了
explicit initialzation list (A1的那种)缺点:public 才可以;只能指定常量;
Point *heap = new Point;
翻译为:
Point *heap = __new(sizeof(Point));
if(heap)heap->Point::Point();
`````
为继承准备:
class Point{
public:
Point(float x=0.,float y=0.):_x(x),_y(y){}
virtual float z();
protected:
float _x,_y;
};
这里并没有定义copy constructor ,copy operator,我们所有member 都是数值,因此默认语义下情况良好。virtual destructor 也没有引入啊,同样道理,行为良好即可
virtual func 的引入给我们的 Point class 带来了膨胀作用:
定义的构造函数被附加了一些代码,用来初始化vptr 这些代码附加在任何base 初始化之后,任何程序员代码之前。例如:
Point* Point::Point(Point* this,float x,float y):_x(x),_y(y){
this->_vptr_Point=_vtbl_Point; this->_x=x;this->_y=y; return this;
}
合成一个copy constructor 和一个 copy operator 而且他们也不再是trival
因为bitwise的赋值可能会带给vptr 错误
···············
继承体系下的对象构造:
如果定义 T object 会发生什么
1.member initialization list 中的data number 初始化操作会放到ctor 中并以声明顺序排列
2.如果一个member没有出现在 list 中但他有default constructor ,他必须被调用
3.在那之前,如果class object有vptr 那么他必须设定初值指向正确vtable
4.在那之前,任何上一层base class ctor 必须被调用,以base class 声明顺序,
如果base class 列于 初始化列表中,参数被传递
如果base class 不再列表,有默认ctor 调用
如果base class 是多重继承下 第二或者后继的base class this 指针调整
5.在那之前virtual base class ctor 必须被调用 从左至右,由深到浅
如果base class 列于 初始化列表中,参数被传递如果base class 不再列表,有默认ctor 调用
base class subobject 的offset 必须在执行期可以被存取
··························
虚拟继承
虚拟继承比较具有特殊性,因为他具有共享性。在这样一个继承体系中
Point 3d 和Vertex 虚拟继承自Point ,此时如果按照普通的ctor 规则。Vertex 的ctor 必须调用Point 的ctor ,然而 当 Point3d 和 Vertex 同为Vertex3d的subobject时,他们的调用一定不能发生(this指针的调整问题) ,而只能有 Vertex3d才可以调用,同样,PVertex构造时候只能由他自己调用,也就是说只有底层的class 完成构建共享的subobject构造。我们可以用一个most _derived参数传给各个构造函数以指示其是否调用共享部分的ctor。
事实上我们发现,只有当object 是完整object时才会调用共享部分的ctor ,而部分subobject不会调用,从而得到新的策略,提供两种构造函数一种供完整的object 一种供subobject调用
~~~~vptr 初始化语义学~~~
c++语言告诉我们:在Point3d ctor 调用size函数,必须决议为 point3d 的size 而不是 pvertex的size,可以理解为在ctor和dtor 中虚函数决议为自己的函数,他不虚啦~~~~~(实际上是因为他没有变身完全)
ctor 调用顺序是从根源到末端,从内而外。当base class ctor 执行时候 derived实体还没有构造出来,在pvertex 构造完整之前,pvertex并不是一个完整的对象,因此只有Point3d subobject构造完毕,这意味着每一个pvertex base class ctor 调用时候,编译器必须保证有适当的 size 函数实体来调用 how,so 决议为自己的size
另一种方法是控制vptr,
在base class ctor 调用之后,但是在程序员提供代码或者initial list 之前,我们设定vptr ,也就是说我们保证vptr指向刚刚构造完毕的base clas subobject 对应的vtable ,保证了它能够调用正确的virtual func;
所以对象构造是这样的过程:
一个 PVertex对象会先形成一个 Point对象- >Point3d->Vertex->Vertex3d->PVertex
ctor执行算法:
1.derived class ctor 中所有 virtual base class 和上一级base class 调用
2. 上述完成vptr 初始化,指向相应的vtable
3.如果有member initial list 的haunted,将在ctor 展开,这些必须在vptr 设定后进行,防止有virtual func 调用
4.最后执行程序员写的代码
vptr 需要被设定的两种情况:
1.对象完整定义后
2.当一个subobject ctor 调用了一个虚拟函数时候(上面说的很明白喽)
当声明一个PVertex对象时候,由于我们对base class ctor 的定义,其vptr 不需要每一个base class ctor 中设定,因此我们可以把ctor 分解成两部分,一种完整的object 实体,一种subobject实体,subobject实体中vptr 设定可以是省略
如果在class ctor initiallist 调用该class 虚拟函数安全么? 理论上安全,但是如果依赖未初始化member 不安全
``````````````````
对象复制语义学 object copy semantics
我们有三种选择:
1 什么都不做,按默认行为实施
2 提供一个explicit copy assignment operator
3 明确拒绝把一个class object 指定给一个class object (声明一个private 的copy assignment operator)
下列情况下,class 不表现出bitwise 的复制语义,也就是说默认的copy operator 是不够的:(和之前介绍的一样)
1.class 有一个member object 而他有copy operator
2.class的base class 有一个copy operator
3.当class 声明virtual func时 (由于要设定vptr when derived to base)
4.class 继承自 virtual base class
同样在这里也面临ctor 中的问题(虚拟继承时),最底层的class 必须调用共享的base class的copy operator,一种策略是,并不压制上面的class 调用自己的copy operator ,也就是说允许了 共享部分的多重复制。另外一种方法是不要允许virtual base class 的copy 操作,甚至不要再任何virtual base class 中声明数据~
··········析构语义学semantics of Destruction
如果class 没有定义dtor 那么只有class 内带的member object 或者自己的base class 有dtor 时候,编译器才会合成一个来,否则dtor 被视为不需要了~~~(有virtual func 也不一定需要的)
dtor 调用顺序是这样的:
1 dtor 函数本身执行,
2 如果class 拥有member class object 而后者拥有dtor 那么他们会以相反的顺序被调用
3 如果object 带有vptr ,现在被重新设定,指向适当的base class vtble
4 如果任意直接的nonvirtual base class 有dtor ,他们会以声明的相反顺序调用
5 如果任意virtual base class 有destructor ,而当前讨论的这个class 是最低端的most –derived class 那么他们会以原来的构造顺序相反顺序调用
一个object 的生命周期结束于dtor 开始执行时,由于每一个base class dtor 轮番调用,所以一个derived object 实际上变成了一个个完整的objec ;一如 PVertex->Vertex3d->Vertex->Point3d->Point
对象的蜕变会因为vptr 重新设定受到影响(dtor中,程序员代码之前),在程序中施行dtor的真正语义会在下一章具体表述~~
#include <time.h>
#include <iostream>
using namespace std;
class memberclass{
public:
memberclass(){cout<<"memberclass default ctor"<<endl;}
memberclass(int x){cout<<"memberclass ctor with parameter"<<endl;}
~memberclass(){cout<<"memberclass dtor"<<endl;}
};
class Point{public: Point(){cout<<"point ctor"<<endl;test();}
~Point(){cout<<"point dtor"<<endl;test();}
virtual void test(){cout<<"point test"<<endl;}
};
class Point3d:virtual public Point{public: Point3d(){cout<<"Point3d ctor"<<endl;test();}
~Point3d(){cout<<"Point3d dtor"<<endl;test();}
virtual void test(){cout<<"Point3d test"<<endl;}};
class vertex:virtual public Point{public: vertex(){cout<<"vertex ctor"<<endl;test();}
~vertex(){cout<<"vertex dtor"<<endl;test();}
virtual void test(){cout<<"vertex test"<<endl;}
};
class vertex3d:public Point3d ,public vertex{public:
memberclass inside;
vertex3d(){cout<<"vertex3d ctor"<<endl;test();}
~vertex3d(){cout<<"vertex3d dtor"<<endl;test();}
virtual void test(){cout<<"vertex3d test"<<endl;}
};
class pvertex:public vertex3d{public:
memberclass inside;
pvertex():inside(3){cout<<"pvertex ctor"<<endl;test();}
~pvertex(){cout<<"pvertex dtor"<<endl;test();}
virtual void test(){cout<<"pvertex test"<<endl;}
};
int main(){
pvertex* p = new pvertex;
delete p;
return 0;
}
输出: guess it ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
point ctor
point test
Point3d ctor
Point3d test
vertex ctor
vertex test
memberclass default ctor
vertex3d ctor
vertex3d test
memberclass ctor with parameter
pvertex ctor
pvertex test
pvertex dtor
pvertex test
memberclass dtor
vertex3d dtor
vertex3d test
memberclass dtor
vertex dtor
vertex test
Point3d dtor
Point3d test
point dtor
point test
---------------------------------------------------OVER--------------------------------------------------------