纯虚函数定义的作用

我们可以为 pure virtual 函数提供定义,但调用它的唯一途径是“调用时明确指出其 class 名称“。

        让我们考虑 XYZ 航空公司设计的飞机继承体系。该公司只有A型和B型两种飞机,两者都以相同方式飞行。因此 XYZ 设计出这样的继承体系:
        class Airport {... } ;
        class Airplane {
                public:
                        virtual void fly(const Airport& destina ion);
        } ;
        void Airplane::fly(const Airport& destination)
        {
                缺省代码,将飞机飞至指定的目的地
        }
        class ModelA: public Airplane {... } ;
        class ModelB: public Airplane {... } ;
        两个classes 共享一份相同性质(也就是它们实现 fly 的方式),所以共同性质被搬到 base class 中,然后被这两个 classes 继承。这个设计突显出共同性质,避免代码重复,并提升未来的强化能力,减缓长期维护所需的成本。所有这些都是面向对象技术如此受到欢迎的原因。XYZ 航空公司应该感到骄傲。
        现在,假设 XYZ 盈余大增,决定购买一种新式C 型飞机。A  型和B 型以及C型有某些不同。更明确地说,它的飞行方式不同。
        XYZ 公司的程序员在继承体系中针对C 型飞机添加了一个 class, 但由于他们急着让新飞机上线服务,竟忘了重新定义其 fly 函数:
class ModelC: public Airplane {
        
} ;
然后代码中有一些诸如此类的动作:
        Airpor POX(...);
        Airplane* pa= new ModelC;
        pa->fly(PDX);
        //未声明 fly 函数
        //POX 是我家附近的机场
        //调用 Airplane: : fly 
        这将酿成大灾难;这个程序试图以 ModelA ModelB 的飞行方式来飞 ModelC 这不是一个可以公开鼓舞旅游信心的行为。
        问题不在 Airplane::fly 有缺省行为,而在千 ModelC 在未明白说出“我要“ 的情况下就继承了该缺省行为。幸运的是我们可以轻易做到“提供缺省实现给 derived classes, 但除非它们明白要求否则免谈”。此间技俩在于切断 “virtual 函数 接口”和其“缺省实现”之间的连接。下面是一种做法:
        class Airplane {
                public:
                        virtual void fly(const Airport& destination)= 0 ;
                protected:
                        void default Fly(cons Airport& destina ion);
        } ;
void Airplane::defaultFly(const Airport& destina ion)
{
        缺省行为,将飞机飞至指定的目的地。
}
        请注意, Airplane: : fly 已被改为一个 pure virtual 函数,只提供飞行接口。其缺省行为也出现在 Airplane class 中,但此次系以独立函数 default Fly 的姿态出现,若想使用缺省实现(例如 ModelA ModelB) ,可以在其 fly 函数中对 defaultFly 做一个 inline 调用(但请注意条款 30 所言, inline 函数和 virtual 函数之间的交互关系):
        class ModelA: public Airplane {
                public:
        ) ;
        virtual void fly(const Airport& destination)
        { defaul tFly (destination) ; }
        class ModelB: public Airplane {
                public:
        } ;
        virtual void fly(const Airpor 七& destination)
        { defaul Fly(destination);)
        现在 ModelC class 不可能意外继承不正确的 fly 实现代码了,因为 Airplane 中的 pure virtual 函数迫使 ModelC 必须提供自己的 fly 版本:
        class ModelC: public Airplane {
                public:
                        virtual void fly(const Airport& destination);
        );
        void ModelC::fly(const Airport& des ination)
        {
                C型飞机飞至指定的目的地
        }
        这个方案并非安全无虞,程序员还是可能因为剪贴 (copy-and-paste) 代码而招来麻烦,但它的确比原先的设计值得倚赖。至于 Airplane: : def ault  Fly, 请注意 它现在成了 protected, 因为它是 Airplane 及其 derived classes 的实现细目。乘客应该只在意飞机能不能飞,不在意它们怎么飞。 Airplane: :defaultFly 是个 non-virtual 函数,这一点也很重要。因为没有任何 一个 derived class 应该重新定义此函数(见条款 36) 。如果 defaultFly virtual 函数,就会出现一个循环问题:万一某些 derived class 忘记重新定义 default Fly, 会怎样?
        有些人反对以不同的函数分别提供接口和缺省实现,像上述的 fly defaultFly 那样。他们关心因过度雷同的函数名称而引起的 class 命名空间污染问题。但是他们也同意,接口和缺省实现应该分开。这个表面上看起来的矛盾该如何解决?唔,我们可以利用 “pure virtual 函数必须在 derived classes 中重新声明,但 它们也可以拥有自己的实现“这一事实。下面便是 Airplane 继承体系如何给 pure virtual 函数一份定义:
        class Airplane {
                public:
                        virtual void fly(const Airport& destination)= 0 ;
};
        void Airplane::fly(const Airport dest ination)
        //pure  virtual 函数实现
        {
                缺省行为,将飞机飞全指定的目的地
        }
        class ModelA: public Airplane {
                public:
                        virtual void fly(cons Airport& destination)
                                { Airplane: :fly(destination); }
        } ;
        class ModelB: public Airplane {
                public:
                        virtual void fly(cons Airport destination)
                                { Airplane: : fly (destinat ion); }
        } ;
        class ModelC: public Airplane {
                public:
                        virtual void fly(const Airport& dest inat ion);
        } ;
        void ModelC::fly(const Airport& dest ination)
        {
                C型飞机飞至指定的目的地
        }
        这几乎和前一个设计一模一样,只不过 pure virtual 函数 Airplane: : fly 替换了独立函数 Airplane: :defaultFly 。本质上,现在的 fly 被分割为两个基本要素: 其声明部分表现的是接口(那是 derived classes 必须使用的),其定义部分则表现出缺省行为(那是 derived classes 可能使用的,但只有在它们明确提出申请时才是)。如果合并 fly defaultFly ,就丧失了“让两个函数享有不同保护级别”的机会: 习惯上被设为 protected 的函数 (defaultFly) 如今成了 public (因为它在 fly 之中)。

                                                                                               ——摘自<<Effective C++(第三版)>>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值