答えは簡単です。C++オブジェクトは、構築されるまで使用できません。基本クラスのサブオブジェクトをまだ作成中の場合は、まだ作成されていないため、派生クラスのオブジェクトを使用できません。(現在構築中のオブジェクトのvptrは、基本クラスコンストラクター内にいる限り、基本クラスvtableを指します。派生クラスコンストラクターに到達するまで更新されません。)
しかし、基本クラスのコンストラクターは、それが通常のオブジェクトに対して呼び出されているのか、サブオブジェクトに対して呼び出されているのかをどのように判断するのでしょうか。まあ、それが世界に関する他のランダムな情報を伝えるのと同じように、あなたはそれを明示的に伝えなければなりません。例えば:
struct Base {
Base() { puts("I am a whole object!"); }
protected:
Base(bool is_subobject) { assert(is_subobject); puts("I'm only part of an object."); }
};
struct Derived : Base {
Derived(): Base(/*is_subobject=*/true) { }
};
あなたがそれについて本当に賢いズボンになりたいのなら、あなたはテンプレートメタプログラミングを使うことができます:
struct Base {
int m_nID;
template<typename T>
Base(T *)
{
#ifdef UNSAFE
// This breaks a lot of rules, but it will work in your case.
// "this" is a Base, not a Derived, so the cast is undefined;
// and if ClassID tried to use any non-static data members,
// you'd be hosed anyway.
m_nID = reinterpret_cast<T*>(this)->T::ClassID();
#else
// This version is guaranteed to work in standard C++,
// but you lose that nice virtual-ness that you worked
// so hard for.
m_nID = T::staticClassID();
#endif
}
virtual int ClassID() { return 1; }
static int staticClassID() { return 1; }
int GetID() { return m_nID; }
};
struct Derived : Base {
Derived(): Base(this) { }
virtual int ClassID() { return 2; }
static int staticClassID() { return 2; }
};
int main() {
Derived cDerived;
std::cout << cDerived.GetID() << std::endl; // prints "2"
}
しかし、私がこの回答を作成しているときにDave Sが言ったように...あなたの例がすべてあなたがしているのであればint nID
、パラメーターとして受け取る保護されたBaseコンストラクターを持ち、仮想メソッドを完全に忘れることができます。