答案很简单:在构造之前,您不能使用任何 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 在我撰写这个答案时所说的那样......如果您的示例就是您所做的一切,那么您可以只使用一个受保护的 Base 构造函数作为int nID
参数,而完全忘记虚拟方法。