Semantics of Construction,Destruction,and copy
考虑如下abstract base class声明: class Abstract_base { public: virtual ~Abstract_base() = 0; virtual void interface() const = 0; virtual const char* mumble() const { return _mumble; } protected: char* _mumble; }; 虽然这个class被设计为一个抽象的base class(其中有纯虚函数,不可能拥有实体),但它仍然需要一个明确的构造函数以初始化其data member _mumble。
纯虚拟函数的存在
我们可以静态调用一个纯虚函数(如Abstract_base::interface())。
关于纯虚析构函数,因为每一个派生类的析构函数会被编译器扩展,以静态的方式调用每一个虚基类以及上一层基类的析构函数,因此只要缺乏任何一个基类析构函数的定义,就会导致链接失败。一个比较好的方案是,不要将虚析构函数声明为纯虚。
虚拟规格的存在
如果将Abstract_base::mumble()设计为一个虚函数,那将是一个糟糕的设计。因为它几乎不会被后继派生类改写。一般而言,把所有的成员函数声明为虚函数,然后由编译器的优化操作把非必要的virtual invocation去除,并不是好的设计观念。
虚拟规则中const的存在
决定一个virtual function是否需要const,是一件琐屑的事情。当你面对一个abstract base class时,却不容易做决定,声明一个函数为const,然后才发现其派生类必须修改一个data member。简单的做法是,不用const就是了。
重新考虑class的声明
class Abstract_base { public: virtual ~Abstract_base(); virtual void interface() const = 0; const char* mumble() const { return _mumble; } protected: Abstract_base(char *pc = 0);//新增一个带唯一参数的构造函数 char* _mumble; };
1、“无继承”情况下的对象构造
对比无继承情况下Point结构体和class(添加默认构造函数)trivial 类函数(构造,析构,拷贝)的情况,可复习此前的笔记即可。
2、继承体系下的对象构造
(略)vptr初始化
如果在集成体系中每一个constructor内调用一个virtual函数,那么每一次调用的都是什么函数呢?C++规则告诉我们,在Point3d constructor中调用的size()(virtual函数),必须为Point3d::size(),即由构造函数(析构函数)中的对象调用一个virtual function,其函数实体应该是在此class中有作用的那个。由于构造顺序是:由根源末端(bottom up),由内而外,base class constructor执行时,derived实体还没有构造出来。因此如果调用操作必须在constructor和destructor中直接调用,那么将每一个调用操作以静态方式决议之,千万不要用到虚拟机制。另一种方法是在constructor和destructor内设置一个标志,以标志确定是否以静态调用方式进行。
vptr在constructor中应该在何时被初始化,我们有三种选择:
答案是2。
- 在任何操作之前
- 在base class constructors调用操作之后,但是在程序员的代码或者是member initialization list中所列的members被初始化之前
- 在每一件事情发生之后
3、Object copy Semantics
当我们以一个class object指定给另一个class object时,我们有三种选择:
- 什么都不做,因此得以实施默认行为
- 提供一个explicit copy assignment operator
- 明确的拒绝把一个class object指定给另一个class object
如果选择第三点,那么只要将copy assignment operator声明为private,并且不提供定义即可。( 余下部分内容前面笔记有记录,主要是在什么情况在位拷贝会无效,虚拟继承部分不做记录)专家建议:不要在任何virtual base class 中声明数据
4、Semantics of Destruction
若class未定义destructor,那么只有在class内的member object或者是base class拥有destructor的情况下,编译器才会合成出一个否则就算class拥有virtual function,也不会合成。为了决定class是否需要程序层面的destructor或是constructor,想想一个class的生命在哪里结束或开始,需要什么操作才能保证对象的完整?这也是constructor和destructor什么时候起作用的关键。例如:对象在使用前必须初始化为某些特定值,这时候需要一个constructor,当我们明确的delete一个对象时,如果没有理由先将其内容清除干净,也不需要归还任何资源,那么不一定需要一个destructor。