メインコンテンツ

AUTOSAR C++14 Rule A10-1-1

Class shall not be derived from more than one base class which is not an interface class

説明

ルール定義

Class shall not be derived from more than one base class which is not an interface class.

根拠

複数の非インターフェイス クラスを継承するクラスは、基本的に、複数の実装にアクセスできます。そのため、コードの保守が困難になる可能性があります。

クラスが複数の非インターフェイス クラスを継承する場合は、同じメンバー関数がそれらの基底クラス内に存在する可能性があるため、派生クラス内でオーバーライドする必要があります。これらの基底クラス自体が共通の基底クラスを継承する (ダイヤモンド構造) 場合は、この可能性が高まります。

インターフェイス クラス Interface に 2 つの具象的な実装 (Impl1Impl2) が含まれており、クラス Final は両方の実装から派生しているとします。クラス階層は次のダイヤモンド構造をしています。

A pictorial representation of the diamond hierarchy. Class 'Final' inherits from the classes 'Imp1' and 'Impl2', which themselves inherit from the class 'Interface'.

以下の問題が発生する可能性があります。

  • あいまいさを排除するために最終派生クラスでオーバーライドが必要となる。

    Impl1Impl2 の両方の実装にクラス Interface のすべてのメソッドのコピーが含まれています。どのコピーが Final オブジェクトを通して呼び出される可能性があるかを明確にするために、通常は、スコープ解決演算子 :: を使用して両方のコピー (または、選択した 1 つのコピー) を明示的に呼び出す Final クラスで、すべてのメソッドのオーバーライドをもう一つ作成します。下の例を参照してください。

    クラス Interface に新しい純粋なバーチャル関数を追加するたびに、直接派生クラスで実装を作成する必要があるだけでなく、クラス階層全体を追跡して、クラス Final でそれらの実装のオーバーライドを作成する必要があります。

    オリジナル クラス Interface がインターフェイス クラスでない場合は、問題がより深刻です。継承がバーチャルでない場合は、Impl1Impl2Interface のメソッドの 2 つのコピーが "暗黙的に" 作成されます (ダイヤモンド問題)。

  • 階層内のすべてのクラスを初期化する責任がある最終派生クラス:

    多重継承における二重初期化を避けるために、C++ 標準では、最上位の派生クラスでそれより前のすべてのクラスのコンストラクターを呼び出す必要があります。

    前述の例で、Final クラスのコンストラクターは、Impl1Impl2 のコンストラクターを呼び出す必要があるだけでなく、それらの親クラスである Interface のコンストラクターも呼び出す必要があります。直接の親を越えてトレースし、最終派生クラスで呼び出すコンストラクターを判別する必要があります。

複数のクラスから 1 つのクラスを派生できるものの、複数のクラスのうち 1 つしか非インターフェイス クラスにできないという制限が多重継承に適用される場合は、このような問題は発生しません。インターフェイス クラスは、純粋なバーチャル関数と、コンパイル時の定数 (静的、contexpr) であるデータ メンバーのみを含むクラスです。このクラスは状態を持ちません。このクラスの唯一の用途は派生クラスによって実装されることです。

多重継承は、1 つのクラスが 1 つの具象実装を拡張するだけでなく、インターフェイス クラスで表される他のアイデアも実装する、という状況に合わせて設計されています。それ以外の用途に多重継承を使用した場合は、保守に危険をもたらす可能性があります。

Polyspace 実装

チェッカーは、複数の基底クラスが非インターフェイス クラスである多重継承にフラグを設定します。

インターフェイス クラスは、純粋なバーチャル関数と、コンパイル時の定数 (静的、contexpr) であるデータ メンバーのみを含むクラスです。コンストラクターまたはデストラクターが =default または =delete に設定されます。

トラブルシューティング

ルール違反が想定されるものの、Polyspace® から報告されない場合は、コーディング規約違反が想定どおりに表示されない理由の診断を参照してください。

すべて展開する

class Interface {
    public:
    virtual void setVal()=0;
};

class Impl1: public Interface{
    int val1;
public:
    void setVal() {
        val1 = 0;
    }
};

class Impl2: public Interface{
    int val2;
public:
    void setVal() {
        val2 = 0;
    }
};

class Final: public Impl1, public Impl2 { //Noncompliant
public:
    void setVal() {
        Impl1::setVal();
        Impl2::setVal();
    } 
    
};

void main() {
    Final finalObj;
    finalObj.setVal();
}

この例では、クラス final がクラス Impl1 とクラス Impl2 から派生しています。Impl1Impl2 の両方のクラスに、コンパイル時定数ではないデータ メンバーと、純粋なバーチャル関数ではないメンバー関数が含まれています。そのため、これらのクラスは非インターフェイス クラスです。2 つの非インターフェイス クラスからの継承は、コーディング ルール違反になります。

チェック情報

グループ: 派生クラス
カテゴリ: Required、Automated

バージョン履歴

R2020a で導入