object 的类型在构造期间发生变化

Type of an object changing during construction

我刚刚发现了以下行为:B 类型的 object 派生自类型 AA 构造期间的最终类型是 A 而不是 B。这可以通过以下示例观察到:

#include <iostream>
#include <typeinfo>

class A
{
    public:
        A() { std::cout << &typeid(*this) << std::endl; }
};

class B : public A
{
    public:
        B() : A() { std::cout << &typeid(*this) << std::endl; }
};

int main()
{
    A a;
    B b;
    return 0;
}

此代码的 运行(使用 gcc 4.8.5 编译)如下:

0x400ae0
0x400ae0
0x400ac0

我们可以看到A::A()中typeid返回的类型是A而不是B,然后最后的类型变成了B

为什么?

是否可以在 parent class 的构造过程中知道 "real" 最终类型?

我的上下文如下:

我有一个 parent class Resource 和几个 class 继承自它。我还有一个 ResourceManager 每次创建资源时都会收到通知,并且必须知道所创建资源的最终类型。我为避免重复代码所做的工作如下,但它不起作用:

class Resource
{
  public:
    Resource() { ResourceManager::notifyCreation(*this); }
    ~Resource() { ResourceManager::notifyDestruction(*this); }
};
class MyResource : public Resource
{
  // I don't have to care to the manager here
};

我知道我可以在 children 的每个 constructor/destructor 中进行通知,但它不太健壮(如果在没有通知管理器的情况下实例化资源,则可能是错误)。 您有解决方法吗?

听起来你要找的是 CRTP

template<typename Concrete>
struct Resource
{
    Resource() { ResourceManager::notifyCreation(*static_cast<Concrete*>(this)); }
    ~Resource() { ResourceManager::notifyDestruction(*static_cast<Concrete*>(this)); }
};

struct MyResource : Resource<MyResource>
{

};

请注意,在调用 notifyCreation 时,MyResource 尚未完成构建。可以获取 MyResource 实例的地址,但这就是可以对该实例执行的所有操作。 (感谢 Caleth 指出这一点)

特别是来自 [class.cdtor]

If the operand of typeid refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.

因此 ResourceManager 必须像这样实现才能使用 typeid

struct ResourceManager
{
    template<typename T>
    void notifyCreation(T&&)
    {
        add(typeid(T));  // can't apply to an expression
    }
    template<typename T>
    void notifyDestruction(T&&)
    {
        remove(typeid(T));  // can't apply to an expression
    }
};

在您的示例中没有在构造函数中执行此操作的好方法,但您可以为 A 提供一个特殊的构造函数,即

 A(const std::type_info &info) {
    std::cout << info.name() << std::endl;
 }

并在 B

B() : A(typeid(*this)) {
    std::cout << typeid(*this).name() std::endl;
}

如果在构造函数外进行,也可以在'A'中提供虚函数,在'B'中覆盖。