派生・継承と仮想関数
隠蔽化の演習を終え、次のオブジェクト指向のステップとして、 派生と継承を説明した。 簡単に仮想関数についても、実演してみた。
基本的なデータに対して、新しく機能を追加したデータを作る場合には、 C言語であれば、基本データ構造におまけデータ部をつけた新しい構造体を 宣言することになる。しかしこの方法では、新しいオマケつきデータが 発生するたびごとに、簡単な処理でも新たな関数を定義しなければならない。
そこで、オマケ付きデータも、同じように使うために派生と継承がある。 新しいオマケを加えたデータ構造をあたらしく作ることを派生と呼ぶ。 元々の基本的データ構造は、一般的に基底クラスと呼び、 追加データを加えたデータ構造は、派生クラスと呼ぶ。 派生クラスでは、基本となるデータ構造の要素やメソッドを使うことができ、 データや処理を流用できることを継承と呼ぶ。
#include #include #include using namespace std ; class Person { private: char name[ 20 ] ; int age ; public: Person( char*n , int a ) { strcpy( name , n ) ; age = a ; } void print() { cout << name << "'s age is " << age << endl ; } } ; class Student : public Person { private: char school[ 20 ] ; public: Student( char *n , int a , char* s ) : Person( n , a ) { strcpy( school , s ) ; } } ; int main() { Person saitoh( "t-saitoh" , 48 ) ; Student aoyama( "aoyama" , 21 , "fnct" ) ; saitoh.print() ; aoyama.print() ; // 継承で表示 return 0 ; }
struct Student : public Person { : // 派生クラスで同じメソッドを新しく作っても良い void print() { Person::print() ; // 基底クラスのprint() を明示して呼び出し cout << "School is " << school << endl ; } } int main() { : // table[] には、基底クラスは同じだけど異なる型が入ってる。 Person* table[ 2 ] = { &saitoh , &aoyama } ; for( int i = 0 ; i < 2 ; i++ ) table[ i ]->print() ; return 0 ; }
この例では、table[i]には、異なる型を代入できている。 しかし、table[i]->print() では、table[1]において、"School is fnct"は 表示されない。 あくまで、table[] に代入する際に、Student型がPerson型に 格下げされている。
table[1]には、Student 型が入っているのだから、"School is fnct"を表示して欲しい場合には どうすればよいのか、こういう時は仮想関数を用いる。 仮想関数では、関数の前に virtual を付加する。
class Person { : virtual void print() { cout << name << "'s age is " << age << endl ; } } ; class Student : public Person { : virtual void print() { Person::print() ; cout << "School is " << school << endl ; } } ;
この様に宣言すると、データ生成時にそれぞれのオブジェクトには、 型を識別する情報が付けられる。 仮想関数を呼び出す時には、データ種別情報を元に、型に応じたメソッドが呼び出される。