有効なC ++項目34:の継承オブジェクト指向(インタフェースの継承と実装の継承を区別するために)

まず、継承されたインターフェイスの治療

  • クラスデザイナーとして、基本クラスのメンバ関数のために、実質的に三つの方法に従って処理することができます。
    • ①純粋仮想関数:基底クラスが実装する派生クラスをさせ、その後、純粋仮想関数を定義
    • ②純粋仮想仮想非仮想関数:基底クラスがオーバーライドして可能にする純粋仮想仮想非仮想関数、派生クラスを定義カバレッジ(オーバーライド)
    • ③共通メンバ関数:ベースクラスは、一般的なメンバ関数を定義したクラスを派生し、非表示にしたくありません
  • この記事では、上記のこれらの3つの設計原則の順序を説明しますここに説明するための基礎として、クラスの次の定義:
class Shape {
public:
    virtual void draw()const = 0; //纯虚函数
    virtual void error(const std::string& msg); //非纯虚函数
    int objectID()const; //普通成员函数
};

class Rectangle :public Shape {};
class Ellipse :public Shape {};

二、純粋仮想関数

  • これは、記事の冒頭で言及した最初のケースです:派生クラスは、彼の純粋仮想関数を実現するための唯一のインターフェース(純粋仮想関数)基底クラスのメンバ関数、派生クラスを継承します
  • いくつかは純粋仮想機能を搭載しています:
    • ①持っているクラスの純粋仮想関数をインスタンス化することはできません
    • ②クラスの純粋仮想関数を持つ、派生クラスは純粋仮想関数を実装する必要があります
  • 上記では、私たちの純粋仮想関数は次のようになります。
class Shape {
public:
    virtual void draw()const = 0; //纯虚函数
};

class Rectangle :public Shape {};
class Ellipse :public Shape {};
  • その目的は、関与する純粋仮想関数です。
    • 形状パターンは、図面の描画()関数を提供するすべてのクラスの基本クラスであるが、原因図のその派生クラス(矩形、円など)は、()関数は、デフォルトの描画を提供するのと同じ方法ではない、したがってことができませんデフォルトの動作で、形状ドローは()が自動的にできるようにするクラスを派生し、純粋仮想関数として定義されます
  • ケースプレゼンテーション:
class Shape {
public:
    virtual void draw()const = 0;
};

class Rectangle :public Shape {
public:
    virtual void draw()const {
        std::cout << "Rectangle" << std::endl;
    }
};
class Ellipse :public Shape {
public:
    virtual void draw()const {
        std::cout << "Ellipse" << std::endl;
    }
};

int main()
{
    //Shape *ps = new Shape;    //错误,不能实例化
    Shape *ps1 = new Rectangle;
    Shape *ps2 = new Ellipse;

    ps1->draw(); //调用Rectangle::draw()
    ps2->draw(); //调用Ellipse::draw()

    return 0;
}
  • 限定使用純粋仮想関数が、それはメカニズムを実現することができ、より安全な通常のデフォルトの実装を提供するために、非純粋仮想仮想仮想関数のために(下の説明を参照してください)

第三に、非純粋仮想仮想仮想関数

ケースプレゼンテーション(プレゼンテーションの利点)

  • プレゼンテーションの場合、仮想関数を初めて目
  • 次のように、飛行機の航空宇宙後継システムを設計する会社、会社今はAとBの両方の航空機を仮定します。
class Airport {}; //机场

class Airplane {  //飞机的基类
public:
    virtual void fly(const Airport& destination) {
        //飞机飞往指定的目的地(默认行为)
    }
};

//A、B两个派生类
class ModelA :public Airplane {};
class ModelB :public Airplane {};
  • 飛ぶ()関数は、AとBが行動を飛んで同じデフォルト2機の航空機を持っているので、仮想関数として宣言されているので、()関数は、フライ飛行機のフライトクラスで、このデフォルトの動作で定義され、その後、AとBを継承しましょう。このような利点は次のとおりです。
    • すべてのプロパティは、基本クラスに移動し、その後、2つのクラスの継承をしましょう
    • コードの重複を避け、強化し、必要な長期的なメンテナンスコストを遅くするために、将来の能力を高めます

ケースプレゼンテーション(純粋仮想関数の代わりに、仮想関数への導入)

  • AとB以上の空港のフライ()関数は、このデフォルトの動作で定義されているので、デフォルトの飛行挙動を有します
  • 次のようにしかし、航空宇宙会社は、C型航空機を追加した場合:
class ModelC :public Airplane {};
  • Cの航空機の飛行には、適切なデフォルトの動作ではないかもしれない場合は、誤ったとえば、フライ()関数で飛行機を呼ぶかもしれません。
Airport PDX;

//C型飞机不具有fly()的行为,但是却调用了fly()
Airplane *pa = new ModelC;
pa->fly(PDX);
  • これは、私たちが「仮想関数」との間の接続をオフコード、カットを変更することができ、設計ミスのコンセプトである「デフォルトの実装です。」コードは以下の通りであります:
class Airport {}; //机场
class Airplane {
public:
    virtual void fly(const Airport& destination) = 0;
protected:
    void defaultFly(const Airport& destination) {
        //飞机飞往指定的目的地(默认行为)
    }
};

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);
    }
};

class ModelC :public Airplane {
public:
    virtual void fly(const Airport& destination) {
        //C型飞机不可以使用默认飞行行为,因此定义自己的飞行方式
    }
};
  • (私たちはdefualtFly内の関数へのパッケージのデフォルトの動作となりますので、フライト)今C航空機や他の航空機が誤って飛行のデフォルトの動作を継承し追加していない、あなたはフライでハエでの動作を定義することができます
  • 注フライdefaultFlyためのAおよびクラスB()関数、()関数がインラインへの呼び出しを行ったに(項目30を参照して、インラインと仮想関数との間の相互作用)こと

ケースプレゼンテーション(純粋仮想関数の代わりに、別の実装の仮想関数)

  • 私たちは、飛ぶ、上記()インタフェースと実装(defaultFly()関数)それが原因発生し、過剰なクラスの名前空間の汚染に関数名が重複してしまうため、別々に達成するために、一部の人は、これに反対することも
  • あなたがいる場合これら二つの行為を分離したくない、それは純粋仮想関数として定義することができるで与えられたdefaultFly()関数の内容。例えば:
class Airport {}; //机场
class Airplane {
public:
    //实现纯虚函数
    virtual void fly(const Airport& destination) = 0 {
        //飞机飞往指定的目的地(默认行为)
    }
};

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) {
        //定义自己的飞行方式
    }
};
  • 上記の機能とデモ例の設計と実装は同じですが、純粋仮想関数飛行機と派生クラス()関数内のハエは::独立した機能飛行機:: defaultFlyを交換飛びます。例えば、defaultFly上記()関数は、公開から保護次のようになります。このような合併はチャンス「の二つの機能は、異なるレベルの保護を楽しみましょう」失われました

第四に、通常のメンバ関数

  • 最後に、通常のメンバ関数の飛行機を見てみましょう
class Shape {
public:
    int objectID()const; //普通成员函数,不希望派生类隐藏
};

class Rectangle :public Shape {};
class Ellipse :public Shape {};
  • 目的通常のメンバ関数:
    • 基底クラスが派生クラスのメンバ関数を非表示にするしないことを意味し
    • それはそれは関係なく、派生クラスのデュオテテ疎外となり、その動作が変更できない示しているので実際には、不変のパフォーマンスの通常のメンバ関数は、その特異性をオーバーライドします
  • 上記のコードで:
    • 各オブジェクトは、オブジェクト識別コードを生成する機能を有している形状
    • この識別子は、オブジェクトIDの定義式を決定::常に形状をしている同じ計算方法である、任意の派生クラスは、動作を変更しないでください
  • 不変性のオーバーライドの普通のメンバーを代表して、このような機能の意味が特定され、そのので、派生クラスで再定義すべきではありません(また、36の用語の議論の焦点です)

五、クラス設計は、多くの場合、2つの間違いを犯します

  • 異なる機能のために、あなたが派生クラスの継承を望むものを指定し、「純粋仮想関数、非純粋仮想仮想仮想関数、通常のメンバ関数」との違いは、経験の浅いクラスの設計者は、最も一般的にコミット次の2つのエラー

最初のエラー

  • 最初の間違いは、「普通のメンバ関数」としてのすべての機能を宣言することで専門の仕事のために派生クラスメートル余分な部屋を作り、
  • 具体的には、非仮想デストラクタの問題を引き起こす(項目7を参照)
  • クラスは基本クラスとして意図されていない場合はもちろん、すべての機能が可能である「普通のメンバ関数」と宣言しました。クラスは、基本クラスになる場合しかし、あなたは(節7を参照)仮想関数の適切な数を宣言することができます
  • あなたは仮想関数のコストから見れば、あなたは80-20ルールを見ることができます(また、句30を参照してください):
    • ルールは:一般的なプログラムは、実行時間の80%は、コード本体の20%に費やされ
    • 平均的に、あなたの関数の呼び出しがなくて、80%の効率を持つことができ、このルール手段は、実質的に仮想のプログラムに影響を与えます。あなたは仮想関数のコストを心配する前にときに、それは本当のキーコードの決定的20%の最初の焦点であります

2番目のエラー

  • 2番目のエラーは、関数のすべてのメンバーが仮想として宣言されていることです
  • 時には当然のように、例えば、インタフェースクラス31の観点インチ ただし、派生クラスで再定義すべきではないいくつかの機能は、あなたが非仮想のものとの関数として宣言しなければなりません

VIの概要

  • インターフェイスの継承と実装の継承異なります。公共の継承の下では、派生クラスは常に継承し、基本クラスのインターフェイス
  • 純粋仮想関数のみ、特定の隠れインターフェイスの継承
  • シンプルな(不純)不純なvirtaul機能はデフォルトインターフェイスの継承と実装の継承を指定します
  • 非仮想関数インタフェースの継承と必須継承を指定します
リリース1504元の記事 ウォンの賞賛1063 ビュー43万+

おすすめ

転載: blog.csdn.net/qq_41453285/article/details/104785357