メインコンテンツ

型に互換性がないためオーバーライドされません

派生クラスのメソッドが virtual 基底クラスのメソッドをオーバーライドせずに隠している

説明

この欠陥は、派生クラスのメソッドが virtual 基底クラスのメソッドと同じ名前でパラメーター数も同じである一方、次の条件に合致する場合に発生します。

  • パラメーター リストで少なくとも 1 つのパラメーター タイプが異なっている。

  • パラメーター リストで const などの修飾子の有無が異なっている。

派生クラスのメソッドは、virtual 基底クラスのメソッドをオーバーライドせずに隠します。

リスク

  • 誤って、基底クラスのメソッドを派生クラスのメソッドでオーバーライドするのではなく隠すことになる可能性があります。

  • 基底クラスのメソッドが隠されており、派生クラスのオブジェクトを使用して基底クラスのパラメーターでメソッドを呼び出した場合は、代わりに派生クラスのメソッドが呼び出されます。渡される引数とタイプが一致しないパラメーターでは、キャストが発生する可能性があります。そうでない場合は、コンパイル エラーが発生します。

修正方法

基底クラスのバーチャル メソッドを派生クラスのメソッドでオーバーライドするには、同じパラメーター リストを使用してメソッドを宣言します。たとえば、必要に応じて、パラメーター タイプの変更や const 修飾子の追加を行います。

C++11 以降では、指定子 override を使用することによって、派生クラス内で意図したオーバーライド元のメソッドを宣言できます。指定子 override を使用して派生クラスのメソッドを宣言した場合は、基底クラスのメソッドと派生クラスのメソッドのパラメーター リストが異なっているとコンパイルが失敗します。派生クラスのメソッドが誤って基底クラスのメソッドを隠す可能性はないため、基底クラスのバーチャル メソッドのオーバーライドは確実に行われます。

それ以外の場合は、派生クラスの宣言に using Base_class_name::method_name という行を追加します。その結果、派生クラスのオブジェクトを使用して基底クラスのメソッドにアクセスできるようになります。

すべて展開する

class Base {
public:
    Base();
    virtual ~Base();
    virtual void func(float i);
    virtual void funcp(float* i);
    virtual void funcr(float& i);
};

typedef double Float;

class Derived: public Base {
public:
    Derived();
    ~Derived();
    void func(Float i);
    void funcp(Float* i);
    void funcr(Float& i);
};

この例では、ステートメント typedef double Float; により、Derived クラスのメソッドの funcfuncp、および funcr は引数 double を取りますが、同じ名前の Base クラスのメソッドは引数 float を取ります。したがって、Base のクラス メソッドに Derived のクラス オブジェクトを使用してアクセスすることはできません。

欠陥は、基底クラスのメソッドを隠すメソッドに表示されます。基底クラスのメソッドのいずれが隠されているかを見つけるには、次のようにします。

  1. 基底クラス定義に移動します。[ソース] ペインで、基底クラス名を右クリックして [定義に移動] を選択します。

  2. 基底クラス定義で、派生クラスのメソッド名と同じ名前をもつ virtual メソッドを特定します。

修正 — 基底クラスのメソッドが隠れないようにする

修正方法の 1 つは、基底および派生クラスのメソッドで同じ型の引数を使用することによるオーバーライドの有効化です。そうせずに、Derived クラス オブジェクトを使用して float 型引数による Base クラス メソッドの呼び出しを行う場合は、Derived クラスの宣言に using Base::method_name を使用した行を追加します。

class Base {
public:
    Base();
    virtual ~Base();
    virtual void func(float i);
    virtual void funcp(float* i);
    virtual void funcr(float& i);
};

typedef double Float;

class Derived: public Base {
public:
    Derived();
    ~Derived();
    using Base::func;
    using Base::funcp;
    using Base::funcr;
    void func(Float i);
    void funcp(Float* i);
    void funcr(Float& i);
};
修正 — override を使用して派生クラスのメソッドを指定する

もう 1 つの修正方法として、指定子 override を使用して、派生クラスのメソッドをオーバーライド元のメソッドとして明示的に指定します。こうすることで、派生クラス内で基底クラスのメソッドをオーバーライドする意図が明確になります。オーバーライド元のメソッドとその基底クラスの対応するメソッドのパラメーター リストが異なる場合は、コードがコンパイルされません。そのため、派生クラスのメソッドで基底クラスのメソッドが隠されることはありません。

class Base {
public:
    Base();
    virtual ~Base();
    virtual void func(float i);
    virtual void funcp(float* i);
    virtual void funcr(float& i);
};

typedef double Float;

class Derived: public Base {
public:
    Derived();
    ~Derived();
//   Compilation error
//  void func(Float i) override;
//  void funcp(Float* i) override;
//  void funcr(Float& i) override;

    void func(float i) override;
    void funcp(float* i) override;
    void funcr(float& i) override;
};

コメント アウトされたメソッド定義とその基底クラスの対応するメソッド定義のパラメーター リストが異なります。派生クラスのメソッドが指定子 override を使用して宣言された場合は、パラメーター リストが異なるため、基底クラスのメソッドが隠されることはありません。その代わり、コードのコンパイルが失敗します。指定子 override を使用する場合は、基底クラスと派生クラス内のバーチャル メソッドのパラメーター リストを同じにする必要があるというルールが適用されます。

namespace Missing_Const {
class Base {
public:
    virtual void func(int) const ;
    virtual ~Base() ;
} ;

class Derived : public Base {
public:
    virtual void func(int) ;

} ;
}

この例では、Base::funcconst 修飾子がありますが、Derived::func にはありません。したがって、Derived::funcBase::func をオーバーライドしません。

修正 — 派生クラスのメソッドに const 修飾子を追加

オーバーライドを有効にするには、派生クラスのメソッド宣言に const 修飾子を追加します。

namespace Missing_Const {
class Base {
public:
    virtual void func(int) const ;
    virtual ~Base() ;
} ;

class Derived : public Base {
public:
    virtual void func(int) const;

} ;
}

意図せず、基底クラスのメソッドが隠されたり、バーチャル メソッドが非バーチャル メソッドに変換されたりしないようにするには、次のようにします。

  • 指定子 virtual を使用して、基底クラス内でバーチャル メソッドを宣言します。

  • 指定子 override を使用して、非最終派生基底クラス内でバーチャル メソッドを宣言します。

  • 指定子 final を使用して、最終クラス内でバーチャル メソッドを宣言します。

namespace Missing_Ref {

class Obj {
    int data;
};

class Base {
public:
    virtual void func(Obj& o);
    virtual ~Base() ;
} ;

class Derived : public Base {
public:
    virtual void func(Obj o) ;

} ;
}

この例では、Derived::funcObj パラメーターを値で受け取りますが、Base::funcObj パラメーターを参照で受け取ります。したがって、Derived::funcBase::func をオーバーライドしません。

修正 — 派生クラスのメソッドのパラメーターに参照を使用

オーバーライドを有効にするには、派生クラスのメソッドのパラメーターを参照によって渡します。

namespace Missing_Ref {

class Obj {
    int data;
};

class Base {
public:
    virtual void func(Obj& o);
    virtual ~Base() ;
} ;

class Derived : public Base {
public:
    virtual void func(Obj& o) ;

} ;
}

意図せず、基底クラスのメソッドが隠されたり、バーチャル メソッドが非バーチャル メソッドに変換されたりしないようにするには、次のようにします。

  • 指定子 virtual を使用して、基底クラス内でバーチャル メソッドを宣言します。

  • 指定子 override を使用して、非最終派生基底クラス内でバーチャル メソッドを宣言します。

  • 指定子 final を使用して、最終クラス内でバーチャル メソッドを宣言します。

結果情報

グループ: オブジェクト指向
言語: C++
既定値: 手書きコードはオン、生成コードはオフ
コマンド ライン構文: VIRTUAL_FUNC_HIDING
影響度: Medium

バージョン履歴

R2015b で導入