プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 24

同カテゴリーの次の記事

プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 25

この記事は、インテル® デベロッパー・ゾーンに公開されている「The Ultimate Question of Programming, Refactoring, and Everything」の日本語参考訳です。


24. override 指定子と final 指定子を使用する

MFC ライブラリーから抜粋した以下のコードについて考えてみます。このエラーは、次の PVS-Studio 診断によって検出されます。

V301 Unexpected function overloading behavior. See first argument of function ‘WinHelpW’ in derived class ‘CFrameWndEx’ and base class ‘CWnd’. (V301 予期しない関数の多重定義です。派生クラス ‘CFrameWndEx’ と基本クラス ‘CWnd’ の関数 ‘WinHelpW’ の第 1 引数を確認してください。)

class CWnd : public CCmdTarget {
  ....
  virtual void WinHelp(DWORD_PTR dwData,
                       UINT nCmd = HELP_CONTEXT);
  ....
};
class CFrameWnd : public CWnd {
  ....
};
class CFrameWndEx : public CFrameWnd {
  ....
  virtual void WinHelp(DWORD dwData,
                       UINT nCmd = HELP_CONTEXT);
  ....
};

説明

仮想関数をオーバーライドする場合、シグネチャーを間違ったり、基本クラスの関数と関係のない新しい関数を定義してしまうことはよくあります。この例には、さまざまなエラーの可能性があります。

  1. オーバーライドされた関数のパラメーターで別の型が使用されています。
  2. オーバーライドされた関数のパラメーター数が異なります。これは、パラメーター数が多い場合、特に重大な問題となります。
  3. オーバーライドされた関数の const 修飾子が異なります。
  4. 基本クラス関数が、仮想クラス関数ではありません。派生クラスの関数が基本クラスのそれをオーバーライドすると仮定されましたが、実際には隠しました。

階層全体の仮想関数シグネチャーを変更する際に、一部の派生クラスを変更し忘れた場合、既存コードの型やパラメーター数の変更でも同様のエラーが発生する可能性があります。

このエラーは、64 ビット・プラットフォームへの移行時に DWORD 型を DWORD_PTR に置き換えたり、LONG 型を LONG_PTR に置き換える場合などに特に起こりやすい過ちです。詳細は、こちら (英語) を参照してください。これはまさに上記のコード例の問題です。

32 ビット・システムでは、DWORDDWORD_PTR はどちらも unsigned long であるため、問題なく動作しますが、64 ビット・システムでは DWORD_PTRunsigned __int64 であるためエラーになります。

正しいコード

class CFrameWndEx : public CFrameWnd {
  ....
  virtual void WinHelp(DWORD_PTR dwData,
                       UINT nCmd = HELP_CONTEXT) override;
  ....
};

推奨事項

このエラーを防ぐ方法があります。次の 2 つの指定子が C++11 で追加されました。

  • override – メソッドが基本クラスの仮想メソッドをオーバーライドすることを示します。
  • final – 派生クラスがこの仮想メソッドをオーバーライドする必要がないことを示します。

この例では、override 指定子を使用して、仮想関数が実際に基本クラス関数をオーバーライドしているかチェックして、オーバーライドしていない場合はエラーを出力するようにコンパイラーに指示します。

CFrameWndEx クラスの WinHelp 関数を決定する際に override を使用すると、アプリケーションの 64 ビット・バージョンをコンパイルする際はエラーとなります。そのため、事前に問題を発見できます。

仮想関数をオーバーライドする場合は、常に override 指定子 (または final 指定子) を使用します。overridefinal の詳細は、以下を参照してください。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

関連記事

  • Parallel Universe マガジンParallel Universe マガジン Parallel Universe へようこそ。 米国インテル社が四半期に一度オンラインで公開しているオンラインマガジンです。インテルの技術者によるテクノロジーの解説や、最新ツールの紹介など、並列化に関する記事を毎号掲載しています。第1号からのバックナンバーを PDF 形式で用意しました、ぜひご覧ください。 12 […]
  • 比較関数の罠比較関数の罠 この記事は、インテル® デベロッパー・ゾーンに公開されている「The Evil within the Comparison Functions」の日本語参考訳です。 この記事の PDF […]
  • 並列プログラミングにおけるロックの効率的な使用並列プログラミングにおけるロックの効率的な使用 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「Using Locks Effectively in Parallel Programming […]
  • インテル® AVX を使用した IIR ガウスぼかしフィルターの実装インテル® AVX を使用した IIR ガウスぼかしフィルターの実装 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「IIR Gaussian Blur Filter Implementation using Intel® Advanced Vector Extensions」の日本語参考訳です。 ソースのダウンロード: gaussian_blur.cpp […]
  • インテル® IPP サンプル – エラーの修正インテル® IPP サンプル – エラーの修正 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「Intel IPP Samples for Windows - error correction」の日本語参考訳です。 この記事は、PVS-Studio を使用することでプログラムがどのように安全になるかを説明した記事の 1 […]