C ++仮想関数を安全にオーバーライドする


100

仮想関数を含む基本クラスがあり、派生クラスでその関数をオーバーライドしたい。派生クラスで宣言した関数が実際に基本クラスの関数をオーバーライドするかどうかをコンパイラに確認させる方法はありますか?古い関数を上書きするのではなく、誤って新しい関数を宣言していないことを保証するマクロまたは何かを追加したいと思います。

この例を見てみましょう:

class parent {
public:
  virtual void handle_event(int something) const {
    // boring default code
  }
};

class child : public parent {
public:
  virtual void handle_event(int something) {
    // new exciting code
  }
};

int main() {
  parent *p = new child();
  p->handle_event(1);
}

parent::handle_event()代わりにここで呼び出されますchild::handle_event()。これは、子のメソッドがconst宣言を欠落しているため、新しいメソッドが宣言されているためです。これは、関数名のタイプミスまたはパラメータータイプのわずかな違いの可能性もあります。また、基本クラスのインターフェイスが変更された場合や、派生クラスが変更を反映するように更新されなかった場合にも、簡単に発生する可能性があります。

この問題を回避する方法はありますか?コンパイラまたは他のツールにこれをチェックするように指示することはできますか?役立つコンパイラフラグ(できればg ++)これらの問題をどのように回避しますか?


2
すばらしい質問です。子クラスの関数が呼び出されない理由を1時間以上把握しようとしています。
Akash Mankar 2014

回答:


89

g ++ 4.7以降では、新しいC ++ 11 overrideキーワードを理解しています。

class child : public parent {
    public:
      // force handle_event to override a existing function in parent
      // error out if the function with the correct signature does not exist
      void handle_event(int something) override;
};

@hirschhornsalz:handle_event関数を実装し、関数の実装の最後にオーバーライドを追加すると、g ++でエラーが発生することがわかりました。あなたがoverrideキーワードに続くクラス宣言でインライン関数実装を提供するならば、すべてがうまくいきます。どうして?
h9uest 2015年

3
override定義では@ h9uestを使用する必要があります。インライン実装は定義と実装の両方なので、問題ありません。
Gunther Piez 2015年

@hirschhornsalzうん、私は同じエラーメッセージをg ++で受け取った。ただし、注意点:あなたとg ++エラーメッセージの両方で「クラス定義」という用語を使用しました-「宣言」({宣言、定義}のペア)を使用すべきではありませんか?あなたは「定義と実装」と言ってこの特定の文脈であなた自身を明らかにしました、しかし私はなぜC ++コミュニティが突然クラスの用語を変更することを決定するのか疑問に思いますか?
h9uest 2015年

20

C#のoverrideキーワードのようなものはC ++の一部ではありません。

gccでは-Woverloaded-virtual、同じ名前の関数で基本クラスの仮想関数を非表示にすることに対して警告しますが、それをオーバーライドしないように十分に異なるシグネチャを使用します。ただし、関数名自体のスペルを間違えたために関数のオーバーライドに失敗するのを防ぐことはできません。


2
Visual C ++を使用している場合
スティーブロウ、

4
Visual C ++を使用してもoverride、C ++ではキーワードは作成されません。ただし、無効なC ++ソースコードをコンパイルできるものを使用している可能性があります。;)
CBベイリー、

3
オーバーライドが無効なC ++であるという事実は、Visual C ++ではなく標準が間違っていることを意味します
Jon

2
@ジョン:わかりました、今あなたが運転していたものがわかります 個人的には、C#スタイルのoverride機能を利用することも、残すこともできました。オーバーライドの失敗に関する問題が発生することはめったになく、それらの診断と修正は比較的簡単です。私が同意しないと思うのは、VC ++ユーザーはそれを使うべきだということです。特定のプロジェクトを移植する必要がない場合でも、C ++がすべてのプラットフォームでC ++のように見えることを望みます。C ++ 0xにはと属性があり[[base_check]]、必要に応じてチェックをオーバーライドするようにオプトインできることは注目に値します。[[override]][[hiding]]
CBベイリー

5
おかしなことに、VC ++を使用しoverrideたコメントの数年後にキーワードが作成さなかったように思われまし。まあ、適切なキーワードではなく、C ++ 11の特別な識別子です。マイクロソフトでは、この特殊なケースを作るためにおよび属性のための一般的なフォーマットに従うことを十分に懸命にプッシュして、override標準:)にそれを作った
デビッド・ロドリゲス- dribeas

18

私の知る限り、それを抽象化することはできませんか?

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

www.parashift.comを読んで、抽象メソッドを実際に実装できると思いました。それは個人的には理にかなっていますが、サブクラスがそれを実装することを強制することが唯一のことです。


これがデストラクタ以上に機能することに気づきました!素晴らしい発見。
ストレージャー、2009年

1
これにはいくつかの潜在的な欠点があります。1)1つ以上のメソッドを抽象としてマークするもう1つのことは、基本クラスをインスタンス化不能にすることです。2)基本クラスは、そもそも変更する必要がないかもしれません。
マイケルバー

3
私はマイケル・バーに同意します。基本クラスの抽象化は問題の一部ではありません。仮想メソッドに機能を持つ基本クラスを作成することは完全に合理的であり、派生クラスでオーバーライドする必要があります。また、別のプログラマーが基本クラスの関数の名前を変更し、派生クラスがその関数をオーバーライドしないようにすることも、同様に合理的です。この場合、Microsoftの「オーバーライド」拡張機能は非常に重要です。残念ながらそれなしでこれを行う良い方法はないので、それが標準に追加されてほしいです。
Brian

これにより、たとえばデフォルト値の場合などBaseClass::method()、派生実装(たとえばDerivedClass::method())で基本メソッド(たとえば)が呼び出されなくなります。
Narcolessico 2018年

11

MSVCでは、CLR override用にコンパイルしていない場合でも、CLR キーワードを使用できます。

g ++では、すべての場合にそれを強制する直接的な方法はありません。他の人々は、を使用して署名の違いを見つける方法について良い答えを出しました-Woverloaded-virtual。将来のバージョンでは、誰かが__attribute__ ((override))C ++ 0x構文を使用して、同等または同等の構文を追加する可能性があります。


9

MSVC ++では、キーワードを使用できますoverride

class child : public parent {
public:
  virtual void handle_event(int something) <b>override</b> {
    // new exciting code
  }
};

override MSVC ++のネイティブコードとCLRコードの両方で機能します。


5

関数を抽象化して、派生クラスがそれをオーバーライドする以外に選択肢がないようにします。

@Rayコードが無効です。

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

抽象関数は、本体をインラインで定義することはできません。になるように変更する必要があります

class parent {
public:
  virtual void handle_event(int something) const = 0;
};

void parent::handle_event( int something ) { /* do w/e you want here. */ }

3

ロジックを少し変更することをお勧めします。何を実行する必要があるかによって、機能する場合と機能しない場合があります。

handle_event()は「退屈なデフォルトコード」を実行できますが、仮想ではなく、「新しいエキサイティングなコード」を実行したい時点で、基本クラスに抽象メソッド(つまり、オーバーライドする必要がある)メソッドを呼び出しますそれはあなたの子孫クラスによって提供されます。

編集:そして、子孫クラスの一部が「新しいエキサイティングなコード」を提供する必要がないと後で決定した場合は、抽象を仮想に変更して、その「挿入された」機能の空の基本クラス実装を提供できます。


2

コンパイラには、基本クラスの関数が非表示になった場合に生成される可能性があるという警告が表示される場合があります。含まれている場合は、有効にします。これは、パラメーターリストのconstの衝突と違いをキャッチします。残念ながら、これはスペルミスを明らかにしません。

たとえば、これはMicrosoft Visual C ++の警告C4263です。


1

C ++ 11 overrideキーワードを派生クラス内の関数宣言で使用すると、宣言された関数が実際に一部の基本クラス関数をオーバーライドしていることをコンパイラーにチェックさせます。そうでない場合、コンパイラーはエラーをスローします。

したがって、override指定子を使用して動的なポリモーフィズム(関数のオーバーライド)を保証できます。

class derived: public base{
public:
  virtual void func_name(int var_name) override {
    // statement
  }
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.