zoukankan      html  css  js  c++  java
  • Effective C++:规定34:区分接口继承和实现继承

    (一个)

    class Shape { 
    public: 
        virtual void draw() const = 0; 
        virtual void error(const string& msg); 
        int objectID() const; 
    }; 
    class Rectangle : public Shape {...}; 
    class Ellipse : public Shape {...};

    公有继承的概念看似简单。似乎非常轻易就浮出水面。然而细致审度之后,我们会发现公有继承的概念实际上包括两个相互独立的部分:函数接口的继承和函数实现的继承。二者之间的区别恰与函数声明和函数实现之间相异之处等价。成员函数的接口总是被继承,由于public继承意味is-a

    pure virtual 函数两个突出特征:(1)必须被不论什么“继承了它们”的具象class又一次声明。(2)它们在抽象class中通常未定义。

    (二)

    声明一个pure virtual函数的目的是为了让derived class仅仅继承函数接口。我能够为它提供一份实现代码,但调用它的唯一途径是“调用时明白指出期class的名称”。

    Sharp* ps1 = new Rectangle;
    ps1->draw();
    Sharp* ps2 = new Rectangle;
    ps2->draw();
    ps1->Shape::draw();//调用Shape的draw
    ps2->Shape::draw();
    (三)

    声明impure virtual函数的目的。是让derived classes继承该函数的接口和缺省实现。

    同意impure virtual函数同一时候指定函数声明和函数缺省行为,却有可能造成危急。

    class Airport {...}; 
    class Airplane { 
    public: 
        virtual void fly(const Airport& destination); 
    };
    void Airplane::fly(const Airport& destination) { 
        //缺省代码,将飞机飞至指定目的地 
    } 
    class ModelA : public Airplane {...};
    class ModelB : public Airplane {...};
    class ModelC : public Airplane {
    ...//未声明fly函数,但它并不希望缺省飞行
    };
    Airport PDX(...); 
    Airplane* pa = new ModelC; 
    ... 
    pa->fly(PDX);  //未说出“我要的情况下就继承了该缺省行为,酿成大灾难”
    这个程序试图用ModelA和ModelB的飞行方式来飞ModelC。
    问题不在Airplane::fly()有缺省行为,而在于ModelC在未明白说出“我要”的情况下就继承了该缺省行为。我们能够做到“提供缺省实现给derived classes。但除非它们明白要求,否则免谈”。

    解决的方法(1):此间伎俩在于切断“virtual 函数接口”和其“缺省实现”之间的连接。以下是一种做法:

    class Airplane { 
    public: 
        virtual void fly(const Airport& destination) = 0; 
        ... 
    protected: 
        void defaultFly(const Airport& destination); 
    }; 
    void Airplane::defaultFly(const Airport& destination) { 
        //缺省行为,将飞机飞至目的地 
    }
    fly已被改成为一个pure virtual函数,仅仅提供飞行接口。缺省行为以defaultFly出如今Airplane class中。

    若想使用缺省实现(比如ModelA和ModelB)。能够在fly中对defaultFly做一个inline调用:

    class ModelA: public Airplane { 
    public: 
        virtual void fly(const Airport& destination) { 
            defaultFly(destination); 
        } 
        ... 
    }; 
    class ModelB: public Airplane { 
    public: 
        virtual void fly(const Airport& destination) { 
            defaultFly(destination); 
        } 
        ... 
    };
    如今ModelC不可能意外继承不对的fly实现代码了。由于Airplane中的pure virtual函数迫使ModelC必须提供自己的fly版本号:

    class ModelC: public Airplane { 
    public: 
        virtual void fly(const Airport& destination); 
        ... 
    }; 
    void ModelC::fly(const Airport& destination) { 
        //将C型飞机飞至指定的目的地 
    }
    像这种话,这个方法并不是安全无虞。程序猿还是可能由于剪贴(copy-and-paste)代码而招来麻烦,但它比原来的设计值得依赖。


    有些人反对以不同的函数分别提供接口和缺省实现,向上述的fly和defaultFly那样。
    所以我们想出了以下这样的解决的方法:

    解决的方法(2):

    我们能够利用“pure virtual函数必须在derived class中又一次声明。但它们能够拥有自己的实现”这一事实。

    以下是Airplane继承体系怎样给pure virtual函数一份定义:

    class Airplane { 
    public: 
        virtual void fly(const Airport& destination) = 0; 
        ... 
    }; 
    void Airplane::fly(const Airport& destination) // pure virtual 函数实现 { 
        //缺省行为,将飞机飞至指定目的地 
    } 
    class ModelA: public Airplane { 
    public: 
        virtual void fly(const Airport& destination) { 
            Airplane::fly(destination); 
        } 
    }; 
    class ModelB: public Airplane { 
    public: 
        virtual void fly(const Airport& destination) { 
            Airplane::fly(destination); 
        } 
    }; 
    class ModelC: public Airplane { 
    public: 
        virtual void fly(const Airport& destination) 
        ... 
    }; 
    void ModelC::fly(const Airport& destination) { 
        // 将C型飞机飞至指定目的地 
    }


    (四)
    假设成员函数是个non-virtual函数,意味着它并不打算在derived classes中有不同的行为。non-virtual 成员函数所表现的不变性凌驾其特异性,不管derived class变得多么特异化。它的行为都不能够改变。


    声明non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现

    class Shape { 
    public: 
        ...
        int objectID() const; 
    }; 
    来看Shape::objectID的声明:能够想做是“每一个Shape对象都有一个用来产生对象识别码的函数:此识别码总是採用同样计算方法,该方法由Shape::objectID的定义式决定,不论什么derived class都不应该尝试改变其行为”。

    请记住:

    1.  接口继承&实现继承不同。在public继承之下。derived classes总是继承base class的接口。

    2.  Pure virtual函数仅仅详细指定接口继承。

    3.  Impure virtual函数详细指定接口继承及缺省实现继承。

    4.  non-virtual函数详细指定接口继承以及强制性实现继承。




    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    client offset screen 的区别
    js中const,var,let区别
    jquery的选择器
    gulp
    JS 实现图片放大效果
    html单个标签实现跑马灯效果
    前端之HTML知识点整理
    各种纯css图标
    防止反复点击的思路
    .NET Memcached Client 扩展获取所有缓存Key
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4735894.html
Copyright © 2011-2022 走看看