2019年6月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

検索・リンク

複雑な継承

課題で取り組んでもらっている、動物の進化を表すクラスの概要を示す。

動物・鳥類・哺乳類クラス

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 動物クラス
class Animal {
private:
char name[ 10 ] ;
public:
Animal( const char s[] ) {
strcpy( name , s ) ;
}
const char* get_name() const { return name ; }
virtual void move() = 0 ;
virtual void birth() = 0 ;
} ;
// 鳥類クラス
class Bird : public Animal {
public:
Bird( const char s[] ) : Animal( s ) {}
virtual void move() {
printf( "%s fry.\n" , get_name() ) ;
}
virtual void birth() {
printf( "%s lay egg.\n" , get_name() ) ;
}
} ;
// 哺乳類クラス
class Mammal : public Animal {
public:
Mammal( const char s[] ) : Animal( s ) {}
virtual void move() {
printf( "%s walk.\n" , get_name() ) ;
}
virtual void birth() {
printf( "%s lay baby.\n" , get_name() ) ;
}
} ;
int main() {
Bird chiken( "piyo" ) ;
chiken.move() ;
chiken.birth() ;
Mammal cat( "tama" ) ;
cat.move() ;
cat.birth() ;
return 0 ;
}
// 動物クラス class Animal { private: char name[ 10 ] ; public: Animal( const char s[] ) { strcpy( name , s ) ; } const char* get_name() const { return name ; } virtual void move() = 0 ; virtual void birth() = 0 ; } ; // 鳥類クラス class Bird : public Animal { public: Bird( const char s[] ) : Animal( s ) {} virtual void move() { printf( "%s fry.\n" , get_name() ) ; } virtual void birth() { printf( "%s lay egg.\n" , get_name() ) ; } } ; // 哺乳類クラス class Mammal : public Animal { public: Mammal( const char s[] ) : Animal( s ) {} virtual void move() { printf( "%s walk.\n" , get_name() ) ; } virtual void birth() { printf( "%s lay baby.\n" , get_name() ) ; } } ; int main() { Bird chiken( "piyo" ) ; chiken.move() ; chiken.birth() ; Mammal cat( "tama" ) ; cat.move() ; cat.birth() ; return 0 ; }
// 動物クラス
class Animal {
private:
  char name[ 10 ] ;
public:
  Animal( const char s[] ) {
    strcpy( name , s ) ;
  }
  const char* get_name() const { return name ; }
  virtual void move() = 0 ;
  virtual void birth() = 0 ;
} ;

// 鳥類クラス
class Bird : public Animal {
public:
  Bird( const char s[] ) : Animal( s ) {}
  virtual void move() {
    printf( "%s fry.\n" , get_name() ) ;
  }
  virtual void birth() {
    printf( "%s lay egg.\n" , get_name() ) ;
  }
} ;

// 哺乳類クラス
class Mammal : public Animal {
public:
  Mammal( const char s[] ) : Animal( s ) {}
  virtual void move() {
    printf( "%s walk.\n" , get_name() ) ;
  }
  virtual void birth() {
    printf( "%s lay baby.\n" , get_name() ) ;
  }
} ;

int main() {
  Bird chiken( "piyo" ) ;
  chiken.move() ;
  chiken.birth() ;
  Mammal cat( "tama" ) ;
  cat.move() ;
  cat.birth() ;
  return 0 ;
}

ここで、カモノハシを作るのであれば、どうすれば良いだろうか?

鳥類・哺乳類とは別にカモノハシを作る

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class SeaBream : public Animal {
public:
Mammal( const char s[] ) : Animal( s ) {}
virtual void move() {
printf( "%s walk.\n" , get_name() ) ;
}
virtual void birth() {
printf( "%s lay egg.\n" , get_name() ) ;
}
} ;
class SeaBream : public Animal { public: Mammal( const char s[] ) : Animal( s ) {} virtual void move() { printf( "%s walk.\n" , get_name() ) ; } virtual void birth() { printf( "%s lay egg.\n" , get_name() ) ; } } ;
class SeaBream : public Animal {
public:
  Mammal( const char s[] ) : Animal( s ) {}
  virtual void move() {
    printf( "%s walk.\n" , get_name() ) ;
  }
  virtual void birth() {
    printf( "%s lay egg.\n" , get_name() ) ;
  }
} ;

この例では、簡単な処理だが、move() の中身が複雑であれば、改めて move() を宣言するのではなく、継承するだけの書き方ができないだろうか?

多重継承

C++ には、複数のクラスから、派生する多重継承という機能がある。であれば、鳥類と哺乳類から進化したのだから、以下のように書きたい。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class SeaBream : public Bird , Mammal {
} ;
class SeaBream : public Bird , Mammal { } ;
class SeaBream : public Bird , Mammal {
} ;

しかし、カモノハシに move() を呼び出すと、鳥類の move() と哺乳類の move() のどちらを動かすか曖昧になる。また、派生クラスは親クラスのデータ領域と、派生クラスのデータ領域を持つため、鳥類の name[] と、哺乳類の name[] を二つ持つことになる。

足と羽のクラス

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Animal {
private:
char name[ 10 ] ;
public:
Animal( const char s[] ) {
strcpy( name , s ) ;
}
const char* get_name() const { return name ; }
virtual void move() = 0 ;
} ;
// 羽
class Wing {
public:
const char* move_method() { return "fly" ; }
} ;
//
class Leg {
public:
const char* move_method() { return "walk" ; }
} ;
class Bird : public Animal , Wind {
public:
Bird( const char s[] ) : Animal( s ) {}
virtual void move() {
printf( "%s %s.\n" , get_name() , move_method() ) ;
}
} ;
class Mammal : public Animal , Leg {
public:
Mammal( const char s[] ) : Animal( s ) {}
virtual void move() {
printf( "%s %s.\n" , get_name() , move_method() ) ;
}
} ;
class Animal { private: char name[ 10 ] ; public: Animal( const char s[] ) { strcpy( name , s ) ; } const char* get_name() const { return name ; } virtual void move() = 0 ; } ; // 羽 class Wing { public: const char* move_method() { return "fly" ; } } ; // class Leg { public: const char* move_method() { return "walk" ; } } ; class Bird : public Animal , Wind { public: Bird( const char s[] ) : Animal( s ) {} virtual void move() { printf( "%s %s.\n" , get_name() , move_method() ) ; } } ; class Mammal : public Animal , Leg { public: Mammal( const char s[] ) : Animal( s ) {} virtual void move() { printf( "%s %s.\n" , get_name() , move_method() ) ; } } ;
class Animal {
private:
  char name[ 10 ] ;
public:
  Animal( const char s[] ) {
    strcpy( name , s ) ;
  }
  const char* get_name() const { return name ; }
  virtual void move() = 0 ;
} ;
// 羽
class Wing {
public:
   const char* move_method() { return "fly" ; }
} ;
// 
class Leg {
public:
   const char* move_method() { return "walk" ; }
} ;
class Bird : public Animal , Wind {
public:
  Bird( const char s[] ) : Animal( s ) {}
  virtual void move() {
    printf( "%s %s.\n" , get_name() , move_method() ) ;
  }
} ;
class Mammal : public Animal , Leg {
public:
  Mammal( const char s[] ) : Animal( s ) {}
  virtual void move() {
    printf( "%s %s.\n" , get_name() , move_method() ) ;
  }
} ;

# うーむ、継承する処理が1行程度でかける処理だと、どのやり方も「継承が便利」というように見えないな…(x_x;

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Animal {
private:
char name[ 10 ] ;
public:
Animal( const char s[] ) {
strcpy( name , s ) ;
}
const char* get_name() const { return name ; }
virtual void move() = 0 ;
virtual void birth() = 0 ;
} ;
// 鳥類クラス
class Bird : public virtual Animal {
public:
Bird( const char s[] ) : Animal( s ) {}
virtual void move() {
printf( "%s fry.\n" , get_name() ) ;
}
virtual void birth() {
printf( "%s lay egg.\n" , get_name() ) ;
}
} ;
// 哺乳類クラス
class Mammal : public virtual Animal {
public:
Mammal( const char s[] ) : Animal( s ) {}
virtual void move() {
printf( "%s walk.\n" , get_name() ) ;
}
virtual void birth() {
printf( "%s lay baby.\n" , get_name() ) ;
}
} ;
class SeaBream : public virtual Bird , virtual Mammal {
public:
SeaBream( const char s[] ) : Animal( s ) {}
void move() {
Mammal::move() ;
}
void birth() {
Bird::birth() ;
}
} ;
class Animal { private: char name[ 10 ] ; public: Animal( const char s[] ) { strcpy( name , s ) ; } const char* get_name() const { return name ; } virtual void move() = 0 ; virtual void birth() = 0 ; } ; // 鳥類クラス class Bird : public virtual Animal { public: Bird( const char s[] ) : Animal( s ) {} virtual void move() { printf( "%s fry.\n" , get_name() ) ; } virtual void birth() { printf( "%s lay egg.\n" , get_name() ) ; } } ; // 哺乳類クラス class Mammal : public virtual Animal { public: Mammal( const char s[] ) : Animal( s ) {} virtual void move() { printf( "%s walk.\n" , get_name() ) ; } virtual void birth() { printf( "%s lay baby.\n" , get_name() ) ; } } ; class SeaBream : public virtual Bird , virtual Mammal { public: SeaBream( const char s[] ) : Animal( s ) {} void move() { Mammal::move() ; } void birth() { Bird::birth() ; } } ;
class Animal {
private:
   char name[ 10 ] ;
public:
   Animal( const char s[] ) {
      strcpy( name , s ) ;
   }
   const char* get_name() const { return name ; }
   virtual void move() = 0 ;
   virtual void birth() = 0 ;
} ;

// 鳥類クラス
class Bird : public virtual Animal {
public:
   Bird( const char s[] ) : Animal( s ) {}
   virtual void move() {
      printf( "%s fry.\n" , get_name() ) ;
   }
   virtual void birth() {
      printf( "%s lay egg.\n" , get_name() ) ;
   }
} ;

// 哺乳類クラス
class Mammal : public virtual Animal {
public:
   Mammal( const char s[] ) : Animal( s ) {}
   virtual void move() {
      printf( "%s walk.\n" , get_name() ) ;
   }
   virtual void birth() {
      printf( "%s lay baby.\n" , get_name() ) ;
   }
} ;

class SeaBream : public virtual Bird , virtual Mammal {
public:
   SeaBream( const char s[] ) : Animal( s ) {}
   void move() {
      Mammal::move() ;
   }
   void birth() {
      Bird::birth() ;
   }
} ;

ただし、多重継承は親クラスの情報と、メソッドを継承する。この場合、通常だと name[] を二つ持つことになるので、問題が発生する。そこで、親クラスの継承に virtual を指定することで、ダイヤモンド型継承の 2つの属性をうまく処理してくれるようになる。

しかし、多重継承は処理の曖昧さや効率の悪さもあることから、採用されていないオブジェクト指向言語も多い。特に Java は、多重継承を使えない。その代わりに interface という機能が使えるようになっている。