#include <iostream>
using namespace std;
class MyClass {
public: // 外部からも派生クラスからも見える
int Get() const;
private: // 外部からも派生クラスからも見えない
protected: // 外部からは見えないが派生クラスからは見える
int m_intval;
};
class MySubClass :
public MyClass // 継承
// アクセス指定子 (省略時はprivateです。大抵の場合publicを選んでおけばよいです):
// - public: 親のpublic,protected,privateをそのまま引き継ぐ
// - private: 親のpublic,protectedをprivateにして引き継ぐ
// - protected: 親のpublicをprotectedにして引き継ぐ
// ↑要するに厳しい条件を採用して引き継ぎます。ちなみに、protected,privateで継承するとアップキャストを禁止できます。
{
public:
MySubClass(int intval);
};
int MyClass::Get() const {
return m_intval;
}
MySubClass::MySubClass(int intval) {
m_intval = intval;
}
int main() {
MySubClass obj(0);
cout << obj.Get() << endl;
/* アップキャスト (upcast) */
/* - 継承木の上 (up) へのキャスト */
/* - 参照先の実体は派生クラス */
/* - 参照を用いて実行可能なのは親クラスができることのみ */
const MyClass& ref = obj; // アップキャストに関してconstは任意
cout << ref.Get() << endl;
const MyClass* ptr = &obj;
cout << ptr->Get() << endl;
return 0;
}
アップキャストした参照を用いると、派生クラスでオーバーライドされたメンバ関数を実行したつもりであっても基底クラスのものが実行されます。派生クラスのメンバ関数を実行させるためには virtual を基底クラスのメンバ関数に付与しておく必要があります。前者の挙動は意図したものではない場合が多いため「オーバーライドするメンバ関数には基底クラスと派生クラスともに virtual を付与」するのが通常の作法です。
#include <iostream>
using namespace std;
class MyClass {
public:
virtual void Say(); // 仮想関数
// virtual void Say() = 0; // 純粋仮想関数 (MyClassは抽象クラスとなり実体を持てなくなる) (*1)
};
class MySubClass :
public MyClass
{
public:
// 「必須ではない」が分かりやすさのために
// virtual を付与するとよい。
virtual void Say();
};
// void MyClass::Say() { // ← 使用予定がなければ実体を実装する必要はない (*1)
// cout << "MyClass::Say" << endl;
// }
void MySubClass::Say() {
cout << "MySubClass::Say" << endl;
}
int main() {
// MyClass obj; // (*1)
MySubClass objSub;
MyClass& objSubRef = objSub;
// obj.Say(); //=> MyClass::Say // (*1)
objSub.Say(); //=> MySubClass::Say
objSubRef.Say(); //=> MySubClass::Say
return 0;
}
以下のようにアップキャストした参照で直接的に実行する場合でなくても、派生クラスでオーバーライドしたメンバ関数が使用されます。
#include <iostream>
using namespace std;
class MyClass {
public:
void Say();
protected: // 派生クラスで見える必要はあるが外部から実行予定はない
virtual void SayBase(); // 仮想関数
};
class MySubClass :
public MyClass
{
protected:
virtual void SayBase();
};
void MyClass::Say() {
SayBase();
}
void MyClass::SayBase() {
cout << "MyClass::SayBase" << endl;
}
void MySubClass::SayBase() {
cout << "MySubClass::SayBase" << endl;
}
int main() {
MyClass obj;
MySubClass objSub;
MyClass& objSubRef = objSub;
obj.Say(); //=> MyClass::SayBase
objSub.Say(); //=> MySubClass::SayBase
objSubRef.Say(); //=> MySubClass::SayBase
return 0;
}
更に、オーバーライドした関数内で、基底クラスの関数を呼び出すこともできます。
#include <iostream>
using namespace std;
class MyClass {
public:
virtual void Say(); // 仮想関数
};
class MySubClass :
public MyClass
{
public:
virtual void Say();
};
void MyClass::Say() {
cout << "MyClass::Say" << endl;
}
void MySubClass::Say() {
MyClass::Say();
cout << "MySubClass::Say" << endl;
}
int main() {
MySubClass obj;
obj.Say(); //=> MyClass::Say
// MySubClass::Say
return 0;
}
引数の型に応じてオーバーロードがなされているメンバ仮想関数を派生クラスでオーバーライドする際には、すべてがオーバーライドされてしまいます。これを避けるためには using を使用します。
#include <iostream>
using namespace std;
class MyClass {
public:
virtual ~MyClass() {}
public:
virtual void Show(int intval) {
cout << "MyClass:Show(" << intval << ")" << endl;
}
virtual void Show(char chval) {
cout << "MyClass:Show(" << chval << ")" << endl;
}
};
class MySubClass :
public MyClass
{
public:
// Show(int) のオーバライド時に Show(char) が消える
virtual void Show(int intval) {
cout << "MySubClass:Show(" << intval << ")" << endl;
}
// 以下の構文で Show(char) を復旧できる
using MyClass::Show;
// 参考: private と併用して Show(*) を使用禁止にできる
// private:
// using MyClass::Show;
};
int main() {
MySubClass obj;
obj.Show(97); //=> MySubClass:Show(97)
obj.Show('a'); //=> MyClass:Show(a)
return 0;
}