2015年5月
« 4月   6月 »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

最近の投稿(電子情報)

アーカイブ

カテゴリー

派生と継承

隠ぺい化の次のステップとして、派生・継承を説明する。

派生を使わずに書くと…

元となるデータ構造(例えばPersonが名前と年齢)でプログラムを作っていて、 途中でその特殊パターンとして、所属と学年を加えた学生(Student)という データ構造を作るとする。

// 元となる構造体(Person)                                                                                                                                                  struct Person {
   char name[ 20 ] ; // 名前
   int  age ;        // 年齢
} ;
// 初期化関数
void set_Person( struct Person* p ,
                 char s[] , int x ) {
   strcpy( p->name , s ) ;
   p->age = x ;
}
// 表示関数
void print_Person( struct Person* p ) {
   printf( "%s %d\n" , p->name , p->age ) ;
}
void main() {
   struct Person saitoh ;
   set_Person( &saitoh , "t-saitoh" , 50 ) ;
   print_Person( &saitoh ) ;
}

パターン1(そのまんま…)

上記のPersonに、所属と学年を加えるのであれば、以下の方法がある。 しかし以下パターン1は、要素名がname,ageという共通な部分があるようにみえるが、 プログラム上は、PersonとPersonStudent1は、まるっきり関係のない別の型にすぎない。

このため、元データと共通部分があっても、同じ処理を改めて書き直しになる。

// 元のデータに追加要素(パターン1)
struct PersonStudent1 {
   char name[ 20 ] ; // 名前
   int  age ;        // 年齢
   char dep[ 20 ] ;  // 所属
   int  grade ;      // 学年
} ;
void set_PersonStudent1( struct PersonStudent1* p ,
                         char s[] , int x ,
                         char d[] , int g ) {
   strcpy( p->name , s ) ; // 同じことを書いてる
   p->age = x ;
   strcpy( p->dep , d ) ;  // 追加分はしかたない
   p->grade = g ;
}
// 名前と年齢だけ表示
void print_PersonStudent1( struct PersonStudent1* p ) {
   // また同じ処理を書いてる
   printf( "%s %d\n" , p->name , p->age ) ;
}
void main() {
   struct PersonStudent1 naka1 ;
   set_PersonStudent1( &naka1 ,
   "naka" , 22 , "PS" , 2 ) ;
   print_PersonStudent1( &naka1 ) ;
}

パターン2(元データの処理を少し使って…)

パターン1では、同じような処理を何度も書くことになり、面倒なので、 元データ用の関数をうまく使うように書いてみる。

// 元のデータに追加要素(パターン2)
struct PersonStudent2 {
   struct Person person ;
   char          dep[ 20 ] ;
   int           grade ;
} ;
void set_PersonStudent2( struct PersonStudent2* p ,
                         char s[] , int x ,
                         char d[] , int g ) {
   // Personの関数を部分的に使う
   set_Person( &(p->person) , s , x ) ;
   // 追加分はしかたない
   strcpy( p->dep , d ) ;
   p->grade = g ;
}
void print_PersonStudent2( struct PersonStudent2* p ) {
   // Personの関数を使う。
   print_Person( &p->person ) ;
}
void main() {
   struct PersonStudent2 naka2 ;
   set_PersonStudent2( &naka2 ,
   "naka" , 22 , "PS" , 2 ) ;
   print_PersonStudent2( &naka2 ) ;
}

このパターン2であれば、元データ Person の処理をうまく使っているので、 プログラムの記述量を減らすことはできるようになった。

しかし、print_PersonStudent2() のような処理は、元データ構造が同じなのに、 いちいちプログラムを記述するのは面倒ではないか?

そこで、元データの処理を拡張し、処理の流用ができないであろうか?

基底クラスから派生クラスを作る

オブジェクト指向では、元データ(基底クラス)に新たな要素を加えたクラス(派生クラス)を 作ることを「派生」と呼ぶ。派生クラスを定義するときは、クラス名の後ろに、 「:」「public/protected/private」基底クラス名を書く。

// 基底クラス
class Person {
private:
   char name[ 20 ] ;
   int  age ;
public:
   Person( const char s[] , int x ) {
      strcpy( name , s ) ;
      age = x ;
   }
   void print() {
      printf( "%s %d\n" , name , age ) ;
   }
} ;
// 派生クラス
class Student : public Person {
private:
   char dep[ 20 ] ;
   int  grade ;
public:
   Student( const char s[] , int x ,
            const char d[] , int g )
            : Person( s , x ) // 基底クラスのコンストラクタ
   {
      strcpy( dep , d ) ;
      grade = g ;
   }
} ;
void main() {
   Person saitoh( "t-saitoh" , 50 ) ;
   saitoh.print() ;
   Student naka( "naka" , 22 , "PS" , 2 ) ;
   naka.print() ;
}

ここで注目すべき点は、main()の中で、Studentクラス"naka"に対し、naka.print() を呼び出しているが、パターン2であれば、print_PersonStudent2()に相当するプログラムを 記述していない。 しかし、この派生を使うと Person の print() が自動的に流用することができる。 これは、基底クラスのメソッドを「継承」しているから、 このように書け、名前と年齢「naka 22」が表示される。

さらに、Student の中に、以下のような Student 専用の新しい print()を記述してもよい。

class Student ...略... {
   ...略...
   void print() {
      Person::print() ;
      printf( "%s %d\n" , dep , grade ) ;
   }
} ;
void main() {
   ...略...
   Student naka( "naka" , 22 , "PS" , 2 ) ;
   naka.print() ;
}

この場合は、継承ではなく機能が上書き(オーバーライト)されるので、 「naka 22 / PS 2」が表示される。

派生クラスを作る際の後ろに記述した、public は、他にも protected , private を 記述できる。

public    だれもがアクセス可能。
protected であれば、派生クラスからアクセスが可能。
派生クラスであれば、通常は protected で使うのが一般的。
private   派生クラスでもアクセス不可。