首页 > 代码库 > 第五章、构造、析构、拷贝语意学
第五章、构造、析构、拷贝语意学
class Abstract_base{
public:
virtual ~Abstract_base()=0;//有问题,链接不通过,不能是纯虚函数
virtual void interface() const =0;//如果改到类的成员变量,最好不要设置为const
//下面函数很糟糕,因为是virtual,函数定义内容不与类型有关,因而几乎不会被后继的derived class改写
virtual const char* mumble()const
{return _mumble;}
protected:
//最好提供构造函数
//Abstract_base(char *mumble_value=http://www.mamicode.com/0):_mumble(mumble_value){}
char *_mumble;
};
class Concrete_derived:public Abstract_base{
public:
void interface();
Concrete_derived();
};
抽象类中可以有非静态的成员变量,此时要在抽象类的构造函数中用初始化列表来初始化它
析构函数不能是(最好不是)纯虚函数,因为每个派生类析构函数会被扩展,以静态调用的方式调用每一个virtual base以及上一层的destructor,缺少任何一个base class destructor就会导致链接失败,而此时如果此时是纯虚函数,则不可能有析构函数来调用。
class TESTA
{
public:
virtual ~TESTA()=0;
};
class TESTB:public TESTA
{
public:
~TESTB(){};
};
调用时 TESTB b;在VS2012中是会出错的。
最上面的类重新设置:
class Abstract_base{
public:
virtual ~Abstract_base();
virtual void interface() =0;//不再是const
//不再是virtual
const char* mumble()const
{return _mumble;}
protected:
Abstract_base(char *mumble_value=http://www.mamicode.com/0):_mumble(mumble_value){}
char *_mumble;
};
1.无继承情况下的对象构造
貌似和前面的章节重复了,忽略
2.继承情况下的对象构造
①记录在member initialization list的data members初始化操作会被放进constructor的函数本身,并以members的声明顺序为顺序。
②如果有一个member没有出现在member initialization list之中,但他有一个默认构造函数,则该默认构造函数被调用。
③如果有虚表,则要设定虚指针
④上一次的基类构造函数必须要调用:如果基类在member initialization list中,那么明确的参数都应该传递过去;如果没有在
member initialization list之中,那么就调用它的默认构造函数。
5所有虚基类构造函数都要被调用,从左到右,从深到浅。
对于虚拟继承,它在构造函数添加一个测试参数来决定是否要调用虚基类的构造函数。
基类Point有两个派生虚继承Point3d,Vertex,而Vertex3d又继承于Point3d,Vertex,最后pVertex继承于Vertex3d
那么可以改写:
Point3d的构造函数为(Point3d *this ,bool __most_derived,float x,float y,float z)只要__most_derived为真时才会调用Point的构造函数。
vptr初始化语意学
如果基类Point有两个派生虚继承Point3d,Vertex,而Vertex3d又继承于Point3d,Vertex,最后pVertex继承于Vertex3d。其中每个类中都要一个虚函数virtual function size()返回该类的大小,如果在各个类的构造函数中调用会直接返回该类的大小;如果在类外使用,则根据具体值来决定。
pVertex pv;
Point3d p3d;
Point *pt=&pv;
pt->size();//返回pVertex大小
pt=&p3d;pt->size();//返回Point3d的大小
但是如果在构造函数中就只能是该类的大小。
构造函数的调用顺序:
①所有虚基类和基类的构造函数会被调用
②vptr被初始化,指向相关的irtual table
③member initialization list在构造函数展开并在最前面
④执行构造函数的程序员的代码
所以说在member initialization list中调用该类的虚拟函数一般是安全的,除非该虚拟函数用到member initialization list中初始化的变量。
3.对象复制语意学
对象赋值(拷贝)函数是为了打开named value return(NVR)
对象赋值操作copy assignment operator的合成条件和构造函数类似
当不要Bitwise Copy Semantics时,类就需要合成一个对象赋值操作:
1.当class内含一个member object,而member object声明有一个copy constructor operator时
2.当class继承一个base class而后者存在有一个copy constructor operator时
3.当class声明了一个或多个virtual functions时,
4.当class派生自一个继承串链,其中有一个或多个virtual base classes时。此时无论基类有没有copy operator。
4.性能比较
5.析构语意学
如果类没有定义析构函数,那么只有在类内袋的成员对象(或是自己的基类)拥有destructor的情况下,编译器才会自动合成出一个来。其他情况不会合成,比如说是指针。
它的执行顺序:
①析构函数本身执行
②如果类有成员对象,则按照声明顺序相反调用它们虚构函数
③如果有vptr,则重新设定,指向适当的base class virtual class
④如果有上一层的非虚继承的基类有析构函数,他们会议声明的顺序相反调用析构函数(在多重继承下从右到左)。
如果有基虚类的析构函数,而当前讨论的这个class是最尾端的class,那么他们会议原来的构造顺序的相反顺序被调用。