????????主要是Effective C++ 中设计到virtual用法的小结。这本书中涉及virtual的条款并不多,主要包括条款07,条款09,条款34,条款35和条款36。先列出这几个条款的标题。
????????07 为多态基类声明virtual析构函数
????????09 绝不在构造和析构函数中调用virtual函数
????????34 区分接口继承和实现继承
????????35 考虑virtual函数以外的其他选择
????????36 绝对不重新定义继承而来的non-virtual函数
????????这里面有几个可以直接在后面加个问号,换成面试题。比如07和09,为什么要为多态基类声明虚析构?为什么不能在构造函数中调用虚函数?答案就是书中比较严谨的叙述,包括例子。但是如何用自己的话语体系来理解?跟着笔者的思路来捋一捋。
????????虚函数到底是用来做什么的?笼统的说,是为了实现运行期多态。但这种回答好像比较流于表面,并未触及灵魂。其实虚函数本质上是一种不确定,这种不确定可以延迟到运行期,根据基类指针指向的对象来确定调用的归属。所以虚函数主要用于多态基类及其继承体系。如果场景中确定不会出现继承机制,那就不要使用virtual函数(废话,但是),一旦有继承场景,务必将基类的析构函数声明为virtual。唯有如此,new出来的派生类对象才能正常释放。那有没有必要将析构函数声明为纯虚函数?取决于你是否将基类对象设计为虚基类。如果你不打算让一个基类拥有实例,那就可以将它的析构函数设置为纯虚函数。不过这时,这个纯析构函数必须有一份实现,否则在派生类析构之后,基类析构找不到定义会出错。
????????其次厘清在继承机制中,pure virtual,impure-virtual以及non-virtual设计的本来用意,这其实是条款34的主要内容。在基类中声明的这三种函数都是为了让基类和派生类拥有统一的接口。但是这三种对派生类的实现有不同的要求。
????????基类中的纯虚函数,基类中可以自己提供一份定义,也可以不提供,让派生类自己实现。总结起来是都可以,但目的是为了让派生类自己实现。书中给了一个例子,就是基类提供一份定义,让派生类来调用基类的实现。从代码的简洁度和安全设计考虑,都是很好的思路。
????????基类中的虚函数,基类中必须提供自己的定义,派生类选择继承或者重写(override)。如果是复写,那就是有多态的条件了。其实主要目的是为了复写,如果所有的派生类都不打算复写这个函数,那应该去掉virtual,把它变为一个non-virtual函数。
????????基类中的non-virtual函数,即普通的成员函数。如上所述,必须是基类和其所有派生类拥有相同的性质,这些性质就放在non-virtual函数中,这样基类写一份实现就好了,不要去复写它。这也是条款36的主要内容。
????????条款35的关注点似乎与其他条款关注点不同。它本质上在描述virtual函数的不足,因为基类和派生类型性质的不同,虽然可以借助虚函数来实现。但是一旦构造它们就性质就确定了,以后没办法改变!有没有办法在构造的时候通过不同的参数,构造不同性质的对象,或者中途改变它们的性质?有的。这就是策略模式。然后介绍了一些策略模式具体的实现方法,可以用函数指针,可以用更抽象的tr1::function,将函数指针,可调用对象等封装起来,使用不同形式的策略模式来解决以上问题。
????????虚函数是在c++面向对象机制中非常重要的技术。是很多设计模式的基础,若能随心所欲,如臻化境,那实践中便可起到四两拨千斤的效果。
|