書籍転載
|
|
Page1
Page2
|
●10.2.3 仮想関数
図10-7のようにStudentのメソッドshow()をPersonのものとは別のものにすることができます。このように、基底クラスのメソッドを派生クラスで再実装することをオーバーライドと呼びます。
図10-7 基底クラスのメソッドを派生クラスでオーバーライドする |
オーバーライドを実現するためには、次の2つの作業が必要です。
- 基底クラスのメソッドを仮想関数にする
- 派生クラスでメソッドを再定義する
オーバーライドは、派生クラスの定義で勝手にできるものではありません。オーバーライドされるメソッドは、基底クラスの定義において仮想関数にしておかなければなりません。メソッドを仮想関数にするのは簡単で、宣言の前にvirtualと書くだけです。クラスPersonの定義を次のように変更します。
|
|
[サンプル]10-virtual1.cpp |
次のようにStudentでメソッドshow()を再定義します。
|
|
[サンプル]10-virtual1.cpp |
オーバーライドの結果を確認しましょう。
|
|
[サンプル]10-virtual1.cpp |
PeronオブジェクトとStudentオブジェクトで、show()の結果が違っていることが確認できます。重要なのは、Person*の配列で、PeronとStudentの両方のオブジェクトを管理していることです。型がPerson*であるにもかかわらず、->show()の結果は適切なものになっています(Studentだけでeat()を再実装してもこうはなりません)。これが継承による多態の実現です。
この例では必要ありませんが、10.1.7項のような独自のデストラクタを利用する場合には、デストラクタを仮想関数にすることを忘れないでください。デストラクタを仮想関数にしないでおくと、派生クラスを多態的に利用したときに、派生クラスのデストラクタではなく基底クラスのデストラクタだけが呼び出されることになります。
●10.2.4 純粋仮想関数
1.1.4項の図1-12のような関係を実装することを考えます(※編集注:本サイト上では該当部分は掲載されていません)。円(Circle)と長方形(Rectangle)を図形(Shape)から継承させることによって、両者を統一的に扱うことができるようにします。PersonとStudentの場合と違うのは、基底クラスであるShapeの描画メソッドdraw()を定義することはできないことです。図形は円や長方形などを一般化したもので、描き方を明示することはできないからです。このようなときには、純粋仮想関数を使います。純粋仮想関数は次のようにキーワードvirtualを使って宣言します。
|
|
[構文]純粋仮想関数 |
純粋仮想関数を利用してShapeとCircle、Rectangleを実装すると次のようになります。
|
|
[サンプル]10-virtual2.cpp |
純粋仮想関数を持つようなクラス(この例ではShape)のオブジェクトを作ることはできません。このようなクラスは抽象クラスと呼ばれ、派生クラスのメンバの仕様や有効範囲等のインターフェイスを規定するために使います。抽象クラスの派生クラス(この例ではCircleとRectangle)では、純粋仮想関数(この例ではdraw())を実装しなければなりません。
非仮想関数と仮想関数、純粋仮想関数の役割をまとめると次のようになります。
- 非仮想関数:派生クラスはインターフェイスと実装の両方を継承する。実装を変更してはいけない
- 仮想関数:派生クラスはインターフェイスと実装を継承するが、実装は変更してもよい
- 純粋仮想関数:派生クラスはインターフェイスだけを継承する
●10.2.5 テンプレートによる暗黙的なインターフェイス指定
継承を使わなくても、CircleクラスとRectangleクラスにメソッドdraw()を持つことを強制することができます。次のコードでは、引数として与えられたオブジェクトのメソッドdraw()を呼び出すテンプレート関数makeDraw()を定義し、CircleオブジェクトとRectangleオブジェクトをその関数に与えています。
|
|
[サンプル]10-generic-programming.cpp |
このコードをビルドするためには、CircleとRectangleはメソッドdraw()を持っていなければなりません。つまり、メソッドdraw()を派生クラスに実装させるという純粋仮想関数の役割の1つを、テンプレート関数makeDraw()でも果たせているのです。
純粋仮想関数を使って、明示的にインターフェイスを指定しなくても、テンプレートを使って暗黙的にインターフェイスを指定できるのです。実は、このような考え方は、本書では既に登場しています。9.1.2項で反復子を紹介した際に、反復子とポインタの両方を引数に取れる関数を作成しました(※編集注:本サイト上では該当部分は掲載されていません)。反復子とポインタの間には、何の継承関係もないにもかかわらずそのようなことが可能なのは、テンプレート関数があるからです。9.3.5項で紹介した関数ポインタを引数にする関数が(※編集注:本サイト上では該当部分は掲載されていません)、10.1.13項のコラムで紹介した関数オブジェクトを引数に取れるのも同じ理由です。
このように、型に依存しない形でプログラムを書くことを、ジェネリックプログラミングと言います。オブジェクト指向プログラミングがプログラミングのパラダイムの1つであるように、ジェネリックプログラミングもプログラミングのパラダイムの1つです。C++は、オブジェクト指向プログラミングを強力にサポートしているのと同じように、ジェネリックプログラミングも強力にサポートしています。実際、STLのような標準ライブラリは、オブジェクト指向プログラミングとジェネリックプログラミングのテクニックを、徹底的に利用しています。このことからも、C++はオブジェクト指向プログラミング言語ではなくマルチパラダイム言語と呼ぶべきだということがわかります。
■
書籍『書籍転載:文法からはじめるプログラミング言語Microsoft Visual C++入門』の部分転載は今回で最終回です。「第10章 クラス〜オブジェクト指向プログラミング」は、この後、「■10.3 C++/CLIにおけるオブジェクト指向プログラミング」という節が続きますが、今回の転載ではカットしました。そのほかの章の内容は、「目次情報ページ」を参照してください。
INDEX | ||
[書籍転載]文法からはじめるプログラミング言語Microsoft Visual C++入門 | ||
C++でオブジェクト指向プログラミング | ||
1.継承/アクセス制御 | ||
2.仮想関数/純粋仮想関数/テンプレートによる暗黙的なインターフェイス指定 | ||
「文法からはじめるプログラミング言語Microsoft Visual C++入門」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|