第15章多態性と虚関数(一)
アップタイプ変換
継承ツリーがベースクラスを頂点とするため、オブジェクトのアドレス(またはポインタまたは参照)を取得し、ベースクラスのアドレスと見なします.これはアップタイプ変換(upcasting)と呼ばれます.
次のコードに示す問題も見られます.
これは、InstrumentだけでなくWindであることを知っているため、所望の出力ではないことは明らかです.この呼び出しはWind::playを出力する必要があります.そのため、Instrumentによって派生されたオブジェクトは、どこにいてもプレイバージョンを使用する必要があります.
関数呼び出しバインディング
コンパイラはInstrumentアドレスのみで正しい呼び出し関数を知らないため、上記のプログラムの問題は早期バンドルによって引き起こされます.
解決策は、実行時に発生し、オブジェクトのタイプに基づいてバンドルされることを意味する遅いバンドル(late binding)と呼ばれます.遅いバンドルは、ダイナミックバンドル(dynamic binding)またはランタイムバンドル(runtime binding)とも呼ばれます.言語が遅いバンドルを実装する場合、実行時にオブジェクトのタイプと適切な呼び出し関数を決定するメカニズムが必要です.これは、コンパイラが実際のオブジェクトタイプを知らないが、正しい関数体を見つけて呼び出すコードを挿入することです.遅いバンドルメカニズムは言語によって異なりますが、いくつかの種類のタイプ情報はオブジェクト自体に組み込まなければならないことが想像できます.その後、どのように動作しているかが表示されます.
かそうかんすう
特定の関数では、遅いバンドルを引き起こすために、C++はベースクラスでこの関数を宣言する際にvirtualキーワードを使用する必要があります.遅いバンドルはvirtualにのみ機能し、ベースクラスのアドレスを使用する場合にのみ発生します.このベースクラスにはvirtual関数がありますが、より早いベースクラスで定義することもできます.
関数宣言時にキーワードvirtualを使用する必要があるだけで、定義時には必要ありません.関数がベースクラスでvirtualとして宣言された場合、すべての派生クラスでvirtualになります.派生クラスにおけるvirtual関数の再定義は、通常、書き換え(override)と呼ばれる.
注:virtualという関数をベースクラスに宣言する必要があります.一致するベースクラス宣言動作をすべて呼び出す派生クラス関数には、虚のメカニズムが使用されます.派生クラスの対応する関数宣言の前にキーワードvirtualを使用することはできますが(これも無害です)、これによりプログラムセグメントが冗長で混乱します.
このファイルはvirtualキーワードを追加したほか、すべてInstrument 2と同じです.cppは同じですが、結果は明らかに違います.現在の出力はWind::playです.
継承ツリーがベースクラスを頂点とするため、オブジェクトのアドレス(またはポインタまたは参照)を取得し、ベースクラスのアドレスと見なします.これはアップタイプ変換(upcasting)と呼ばれます.
次のコードに示す問題も見られます.
//: C15:Instrument2.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
// Inheritance & upcasting
#include <iostream>
using namespace std;
enum note { middleC, Csharp, Eflat }; // Etc.
class Instrument {
public:
void play(note) const {
cout << "Instrument::play" << endl;
}
};
// Wind objects are Instruments
// because they have the same interface:
class Wind : public Instrument {
public:
// Redefine interface function:
void play(note) const {
cout << "Wind::play" << endl;
}
};
void tune(Instrument& i) {
// ...
i.play(middleC);
}
int main() {
Wind flute;
tune(flute); // Upcasting
} ///
:
Instrument::play
これは、InstrumentだけでなくWindであることを知っているため、所望の出力ではないことは明らかです.この呼び出しはWind::playを出力する必要があります.そのため、Instrumentによって派生されたオブジェクトは、どこにいてもプレイバージョンを使用する必要があります.
関数呼び出しバインディング
コンパイラはInstrumentアドレスのみで正しい呼び出し関数を知らないため、上記のプログラムの問題は早期バンドルによって引き起こされます.
解決策は、実行時に発生し、オブジェクトのタイプに基づいてバンドルされることを意味する遅いバンドル(late binding)と呼ばれます.遅いバンドルは、ダイナミックバンドル(dynamic binding)またはランタイムバンドル(runtime binding)とも呼ばれます.言語が遅いバンドルを実装する場合、実行時にオブジェクトのタイプと適切な呼び出し関数を決定するメカニズムが必要です.これは、コンパイラが実際のオブジェクトタイプを知らないが、正しい関数体を見つけて呼び出すコードを挿入することです.遅いバンドルメカニズムは言語によって異なりますが、いくつかの種類のタイプ情報はオブジェクト自体に組み込まなければならないことが想像できます.その後、どのように動作しているかが表示されます.
かそうかんすう
特定の関数では、遅いバンドルを引き起こすために、C++はベースクラスでこの関数を宣言する際にvirtualキーワードを使用する必要があります.遅いバンドルはvirtualにのみ機能し、ベースクラスのアドレスを使用する場合にのみ発生します.このベースクラスにはvirtual関数がありますが、より早いベースクラスで定義することもできます.
関数宣言時にキーワードvirtualを使用する必要があるだけで、定義時には必要ありません.関数がベースクラスでvirtualとして宣言された場合、すべての派生クラスでvirtualになります.派生クラスにおけるvirtual関数の再定義は、通常、書き換え(override)と呼ばれる.
注:virtualという関数をベースクラスに宣言する必要があります.一致するベースクラス宣言動作をすべて呼び出す派生クラス関数には、虚のメカニズムが使用されます.派生クラスの対応する関数宣言の前にキーワードvirtualを使用することはできますが(これも無害です)、これによりプログラムセグメントが冗長で混乱します.
//: C15:Instrument3.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
// Late binding with the virtual keyword
#include <iostream>
using namespace std;
enum note { middleC, Csharp, Cflat }; // Etc.
class Instrument {
public:
virtual void play(note) const {// virtual
cout << "Instrument::play" << endl;
}
};
// Wind objects are Instruments
// because they have the same interface:
class Wind : public Instrument {
public:
// Override interface function:
void play(note) const {// virtual,
cout << "Wind::play" << endl;
}
};
void tune(Instrument& i) {
// ...
i.play(middleC);
}
int main() {
Wind flute;
tune(flute); // Upcasting
system("pause");
} ///:
このファイルはvirtualキーワードを追加したほか、すべてInstrument 2と同じです.cppは同じですが、結果は明らかに違います.現在の出力はWind::playです.