2010年5月9日日曜日

コンストラクタで仮想関数を呼び出す(C++)

C++ではコンストラクタで仮想関数を呼び出しても意図した動作にならないんですよね、どんな動きになるかといいますと、オーバーライドした関数ではなく基底クラス(親クラス)の仮想関数が呼び出されてしまいます。
検証用のコードを用意しましたのでまずはクラス図を見てみましょう。

単純な継承関係のクラスを二つ用意しました。init()は仮想関数になっています。
つぎにソースを見てみましょう、用意したのは次のファイルです。
  • main.cpp
  • Character.h
  • CharacterMonster.h
まずはCharacter.hです。
#ifndef _CHARACTER_H_
#define _CHARACTER_H_

#include 
class Character
{
public:
    // constructor
    Character() {
        this->init();
    }

    // destructor
    ~Character() {
        // do nothing...
    }

protected:
    virtual void init() {   //  仮想関数で定義
        printf("init Character\n");
    }
};
#endif

次にCharacterMonster.hです。
#ifndef _CHARACTER_MONSTER_H_
#define _CHARACTER_MONSTER_H_

#include "Character.h"

class CharacterMonster : public Character
{
public:
 // constructor
 CharacterMonster() {
  // do nothing...
 }

 // destructor
 virtual ~CharacterMonster() {
  // do nothing...
 }

protected:
 virtual void init() { // オーバーライド
  printf("init Character Monster\n");
 }
};
#endif

最後にmain.cppです。
#include "CharacterMonster.h"
#include 

void main(void)
{
    Character* character = new CharacterMonster();
    delete character;

    printf("hit any key...");
    while(getchar() == EOF){}
}
init()はオーバーライドされているのでこのプログラムを実行すると「init Character Monster」が表示されると思いきや「init Character」が表示されてしまいます。
ということでコンストラクタで仮想関数を呼び出しても意図した動作になりませんでした。
ちなみにinit()を純粋仮想関数にした場合は実体がないためリンクエラーとなります。

なのでinit()メソッドをいつも用意するようにしているのですけどinit()を呼び出すのを忘れたりもするんですよね、ケースバイケースなのかなぁ?。

0 件のコメント:

コメントを投稿