2017-07-14 120 views
5

期間改變的我剛discoverd以下行爲:具有A類型派生B類型的對象,的A施工時的最終類型是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 

我們可以看到,通過在typeid的返回的類型A::A()A而不是B,然後最終類型更改爲B

爲什麼?

是否有可能在構建父類時知道「真正」最終類型?

我的背景是這樣的:

我有一個父類Resource和幾類從它繼承。我也有一個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 
}; 

我知道我可以做的通知,每個構造的孩子/析構函數,但它是不太可靠的(可能的錯誤如果一個資源沒有通知管理者即時通知)。 你有任何想法的解決方法?

+2

嗯......'&typeid(....)'? – WhiZTiM

+3

'A''''''''''''''你爲什麼期望'typeid'有所不同? –

+0

@WhiZTiM爲什麼? typeid的返回值在每種類型的內存中都是唯一的。 – Caduchon

回答

5

聽起來像你要找的是什麼CRTP

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

struct MyResource : Resource<MyResource> 
{ 

}; 

注意MyResource是尚未完成由以notifyCreation通話時建造。可以採用MyResource實例的地址,但這是關於實例可以做的所有事情。 (感謝Caleth指出這)

特別地從[class.cdtor]

如果typeid操作數指的是在建或破壞和靜態類型的操作數的對象既不構造或析構函數的類也沒有一個基地,行爲是不確定的。

因此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 
    } 
}; 
+0

如果'MyResource'沒有超過'Resource'的成員,這只是安全的。 – Caleth

+1

@Caleth你的意思是沒有更多的基類?是的,但代碼OP顯示看起來像這樣,我沒有繼續推測OP想要什麼 –

+0

不,我的意思是沒有更多的數據成員。它們在調用'ResourceManager :: notifyCreation'時沒有被初始化,並且當'ResourceManager :: notifyDestruction'被調用時被銷燬 – Caleth

1

還有就是做一個構造函數在你的例子沒有什麼好辦法,但你可以提供一個特殊的構造對於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」覆蓋。