前回のコンストラクタがおかしくなったのは何故だったのでしょうか? 今回はそのことについて話したいと思います。
今回の要点は以下の通りです。
では、いってみましょう。
前回、コンストラクタを使ったときに仮想関数の動作がおかしくなりました。何でこうなったんでしょうかね?
もう一度前回のおかしくなった部分を見てみましょう。
// File.h class CFile { public: CFile(const char* pszPath, const char* pszFlags); }; |
// File.cpp CFile::CFile(const char* pszPath, const char* pszFlags) { m_pfile = NULL; m_bCopy = false; Open(pszPath, pszFlags); } |
// TextFile.h class CTextFile { public: CTextFile(const char* pszPath, const char* pszFlags); }; |
// TextFile.cpp CTextFile::CTextFile(const char* pszPath, const char* pszFlags) : CFile(pszPath, pszFlags) { } |
先ず、CFile のコンストラクタを呼びます。そして、その中でメンバを初期化して、Open 関数を呼んでいます。ここの Open 関数で ModifyFlags 関数が正しく呼ばれないのでした。
実は、これは仮想関数の初期化がコンストラクタで行われるということのせいなのです。
「仮想関数の初期化とは何?」と思うと思います。実は、仮想関数の呼び出しには「仮想関数テーブル」という隠しメンバ変数を使っています。この初期化がコンストラクタで行われるということです。
つまり、CFile のコンストラクタ内では、まだ仮想関数テーブルは CFile によって初期化されただけなのです。なので、CFile の ModifyFlags が呼ばれてしまうというわけです。
で、CTextFile のコンストラクタに戻ってきたときに、やっと CTextFile による初期化が行われるのです。仮想関数テーブルを上書きしてしまうわけですね。ここでようやく仮想関数が正しく呼ばれます。
つまり、上の CTextFile のコンストラクタはこういう風にしなければいけないわけです。
// TextFile.cpp CTextFile::CTextFile(const char* pszPath, const char* pszFlags) { Open(pszPath, pszFlags); } |
まとめると、
ということです。
何故こうなっているかは、こう考えてみると分かります。
CTextFile に初期化するべき変数があったとします。そして、その変数を仮想関数内で使っているとします。
もし、CFile のコンストラクタ内でその仮想関数が呼べてしまうと、まだ CTextFile のコンストラクタは実行されていないので、初期化していない変数を使ってしまうことになります。とても危ないですね。
なので、使えないようなっているわけです。面倒なようですが、理にはかなっているわけです。
ということで、仮想関数をコンストラクタで使うときは十分注意して下さい。
短いですが、今回はこれで終わりです。では、今回の要点です。
次回は、仮想関数の中の仮想関数、純粋仮想関数について話したいと思います。
では、さようなら。
Last update was done on 2000.9.7
この講座の著作権はロベールが保有しています