第5章 构造,解构,拷贝语意学 (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(其中有pure virtual function,使得Abstract_base不可能拥有实体),但它仍然需要一个明确的构造函数以初始化其data member _mumble.如果没有这个初始化操作,其derived class
的局部性对象_mumble将无法决定初值,例如:
class Concrete_derived : public Abstract_base { public: Concrete_derived(); }; void foo() { // Abstract_base::_mumble未被初始化 Concrete_derived trouble; }
可能说是:也许Abstract_base的设计者试图让其每一个derived class 提供_mumble的初值.然而如果这样,derived class 的唯一要求就是Abstract_base必须提供一个带有唯一参数的 protected constructor:
Abstract_base::Abstract_base(char *mumble_val = 0) : _mumble(mumble_value) {}
一般而言,class 的data member应该初始化,并且只在constructor中或是在 class 的其他member functions中指定初值,其他任何操作都将破坏封装性质,使 class 的维护和修改更加困难.
当然也可能是说设计者的错误并不在于未提供一个 explicit constructor,而是不应该在抽象的base class 中声明data member.这是比较强而有力的论点(把interface和implementation分离),但它并不是行遍天下皆有理,因为将"被共享的数据"抽取出来放在base class 中,毕竟是一种正当的设计.
纯虚拟函数的存在 (Presence of Pure Virtual Function)
C++可以定义和调用(invoke)一个pure virtual function;不过它只能被静态地调用(invoke statically),不能经由虚拟机制调用.例如,可以合法写下这段代码:
// OK:定义pure virtual function,但只能被静态地调用(invoked statically) inline void Abstract_base::interface() const { function // ... } inline void Concrete_derived::interface() const { // OK:静态调用(static invocation) Abstract_base::interface(); function // ... }
要不要这样做,全由 class 设计者决定.唯一的例外就是pure virtual destructor:class 设计者一定得定义它.为什么?因为每一个derived class destructor会被编译器加以扩展,以静态调用的方式调用其"每一个virtual base class"以及"上一层base
class"的destructor.因此,只要缺乏任何一个base class desstructors的定义,就会导致链接失败.
难道对一个pure virtual destructor的调用操作,不应该在"编译器扩展derived class的destructor"时压抑下来吗?不!class 设计者可能已经真的定义了一个pure virtual destructor(就像上一个例子中定义了Abstract_base::interface()那样).这样的设计是以C++语言的一个保证为前提:继承体系中每一个 class object的destructor都会被调用,所以编译器不能够压抑这个调用操作.
一个比较好的替代方案就是,不要把 virtual destructor声明为pure.
虚拟规格的存在 (Presence of a Virtual Specification)
如果决定把Abstract_base::mumble()设计为一个 virtual function,那将是一个糟糕的选择,因为其函数定义内容并不与类型有关,因而几乎不会被后继的derived class 改写.此外,由于它的non-virtual 函数实体是一个 inline 函数,如果常常被调用的话,效率上的负担实在不低.
然而,编译器难道不能经由分析,知道该函数只有一个实体存在于 class 层次体系中?果真如此的话,难道它不能够把调用操作转换为一个静态调用操作(static invocation),以允许调用操作的 inline expansion?如果 class 层次体系陆续被加入新的 class,带有这个函数的新实体,又当如何?是的,新的 class 会破坏优化.该函数现在必须被重新编译(或是产生第二个--也就是多态--实体,编译器将通过流程分析哪一个实体要被调用).不过,函数可以以二进制形式存在与一个library中.
一般而言,把所有的成员函数都声明为 virtual function,然后再靠编译器的优化操作把非必要的 virtual invocation去除,并不是好的设计观念.
虚拟规格中的 const 的存在
决定一个 virtual function是否需要 const,似乎是件琐碎的事情.但当真的面对一个abstract base class 时,却不容易做决定.做这件事情,意味着得假设subclass实体可能被无穷次数地使用.不把函数声明为 const,意味着该函数能够获得一个 const reference或 const pointer.
重新考虑 class 的声明
由前面的讨论可知,重新定义Abstract_base如下,才是比较适当的一种假设:
class Abstract_base { public: virtual ~Abstract_base(); // 不再是pure virtual void interface() = 0; const char *mumble() const { return _mumble; } // 不再是virtual protected: Abstract_base(char *pc = 0); // 新增一个带有唯一参数的constructor char *_mumble; };
版权声明:本文为博主原创文章,未经博主允许不得转载。