C++関数継承の詳細解説:「ダイヤモンド継承」問題を回避するには?

WBOY
リリース: 2024-05-02 11:45:02
オリジナル
632 人が閲覧しました

ダイヤモンド継承の問題: 派生クラスが同時に複数の基本クラスから同じ関数を継承するときに発生する問題は、どの関数のバージョンを呼び出すかを決定できません。解決策: 仮想継承: 基本クラスの仮想テーブル ポインターを作成して、関数呼び出しが常に最も具体的な基本クラス実装を指すようにします。実際のケース: Cylinder クラスは Circle および Rectangle から継承し、仮想継承を使用してひし形の継承を回避し、Cylinder クラスの getArea() 関数実装が常に呼び出されるようにします。

C++ 函数继承详解:如何避免“钻石继承”问题?

C 関数の継承の詳細な説明: 「ダイヤモンド継承」の扱い

はじめに

関数の継承は、派生クラスが基本クラスの関数にアクセスして再利用できるようにする C の強力な機能です。しかし、複数の基底クラスが同じ機能を持つ場合、「ダイヤモンド継承」と呼ばれる問題が発生する可能性があります。この記事ではダイヤモンドの相続とその解決策について解説し、実際の事例を紹介します。

ダイヤモンドの継承

ダイヤモンドの継承は、派生クラスが 2 つ以上の基本クラスから同じ関数を同時に継承するときに発生します。その結果、派生クラスで呼び出された関数のバージョンを特定できなくなります。

class Base1 {
public:
    void print() {
        std::cout << "Base1 print" << std::endl;
    }
};

class Base2 {
public:
    void print() {
        std::cout << "Base2 print" << std::endl;
    }
};

class Derived : public Base1, public Base2 {
public:
    void print() {
        // 调用哪个基类的 print() 函数?
    }
};
ログイン後にコピー

上記の例では、Derived クラスは Base1Base2 を継承しており、どちらの基本クラスも同じ print( )### 関数。 Derived::print() が呼び出されるとき、Base1::print()Base2::print() のどちらが呼び出されるのかを判断できません。

ダイヤモンド継承を回避する

ダイヤモンド継承を回避する一般的な解決策は、仮想継承を使用することです。仮想継承では、基本クラス オブジェクトをコピーする代わりに、基本クラスへの vtable ポインターを作成します。これにより、派生クラスへの関数呼び出しが常に最も具体的な基本クラス実装を指すようになります。

class Base1 {
public:
    virtual void print() {
        std::cout << "Base1 print" << std::endl;
    }
};

class Base2 {
public:
    virtual void print() {
        std::cout << "Base2 print" << std::endl;
    }
};

class Derived : public virtual Base1, public virtual Base2 {
public:
    void print() override {
        std::cout << "Derived print" << std::endl;
    }
};
ログイン後にコピー

上の例では、

Base1Base2 が仮想継承を使用します。これにより、Derived::print() が常に Derived クラスの実装を呼び出すようになります。

実践例

グラフィックの面積を計算する例を考えてみましょう。面積を計算するための

getArea() 関数を定義する基本クラス Shape があります。また、形状固有の面積計算を提供する 2 つの派生クラス CircleRectangle もあります。

class Shape {
public:
    virtual double getArea() = 0;
};

class Circle : public Shape {
public:
    Circle(double radius) : _radius(radius) {}
    double getArea() override {
        return 3.14 * _radius * _radius;
    }
private:
    double _radius;
};

class Rectangle : public Shape {
public:
    Rectangle(double width, double height) : _width(width), _height(height) {}
    double getArea() override {
        return _width * _height;
    }
private:
    double _width;
    double _height;
};
ログイン後にコピー

「スリーブ」形状を実装するために、

CircleRectangle を継承する派生クラス Cylinder を作成しました。ただし、CircleRectangle の両方に getArea() 関数があるため、Cylinder はダイヤモンド継承の問題に直面します。

class Cylinder : public Circle, public Rectangle {
public:
    Cylinder(double radius, double height) : Circle(radius), Rectangle(radius, height) {}
};
ログイン後にコピー

ダイアモンド継承を回避するために、仮想継承を使用します。

class Cylinder : public virtual Circle, public virtual Rectangle {
public:
    Cylinder(double radius, double height) : Circle(radius), Rectangle(radius, height) {}
};
ログイン後にコピー
これで、

Cylinder クラスの getArea() 関数が常に呼び出されます。派生した最も具体的なクラスの実装 (つまり、Cylinder)。

以上がC++関数継承の詳細解説:「ダイヤモンド継承」問題を回避するには?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート