回答
有你沒有任何線索,dynamic_cast
和static_cast
不像const_cast
或reinterpret_cast
,他們實際上執行指針算術和有些類型安全的理由。
的指針運算
爲了說明這一點,認爲以下設計:
struct Base1 { virtual ~Base1(); char a; };
struct Base2 { virtual ~Base2(); char b; };
struct Derived: Base1, Base2 {};
的Derived
實例應該是這個樣子(它是基於GCC因爲它實際上是依賴於編譯器...):
| Cell 1 | Cell 2 | Cell 3 | Cell 4 |
| vtable pointer | a | vtable pointer | b |
| Base 1 | Base 2 |
| Derived |
現在想一下爲c asting:
- 鑄造從
Derived
到Base1
不需要任何額外的工作,他們是在同一個物理地址 - 鑄造從
Derived
到Base2
必要指針由2個字節
因此轉移,有必要知道能夠在一個派生對象和它的一個基礎之間進行轉換的對象的內存佈局。這只是編譯器所知道的,信息不能通過任何API訪問,也不是標準化的或者其他的。
在代碼中,這將轉化這樣的:
Derived derived;
Base2* b2 = reinterpret_cast<Base2>(((char*)&derived) + 2);
而這,當然,對於一個static_cast
。
現在,如果您在的實現中能夠使用static_cast
,那麼您可以利用編譯器並讓它爲您處理指針算術......但您仍然沒有擺脫困境。
Writing dynamic_cast?第一
第一件事,我們需要澄清的dynamic_cast
規格:
dynamic_cast<Derived*>(&base);
,則返回nullbase
不是Derived
一個實例。dynamic_cast<Derived&>(base);
在這種情況下拋出std::bad_cast
。dynamic_cast<void*>(base);
返回最派生類dynamic_cast
尊重訪問規格(public
,protected
和private
繼承)
我不知道你的地址,但我認爲這將是醜陋。使用typeid
不足以這裏:
struct Base { virtual ~Base(); };
struct Intermediate: Base {};
struct Derived: Base {};
void func()
{
Derived derived;
Base& base = derived;
Intermediate& inter = dynamic_cast<Intermediate&>(base); // arg
}
的這裏的問題是,typeid(base) == typeid(Derived) != typeid(Intermediate)
,所以你不能依賴,要麼。
另一個有趣的事情:
struct Base { virtual ~Base(); };
struct Derived: virtual Base {};
void func(Base& base)
{
Derived& derived = static_cast<Derived&>(base); // Fails
}
static_cast
不工作時,虛擬繼承參與......所以我們一直走的指針運算計算匍匐問題
一個幾乎解決方案。
class Object
{
public:
Object(): mMostDerived(0) {}
virtual ~Object() {}
void* GetMostDerived() const { return mMostDerived; }
template <class T>
T* dynamiccast()
{
Object const& me = *this;
return const_cast<T*>(me.dynamiccast());
}
template <class T>
T const* dynamiccast() const
{
char const* name = typeid(T).name();
derived_t::const_iterator it = mDeriveds.find(name);
if (it == mDeriveds.end()) { return 0; }
else { return reinterpret_cast<T const*>(it->second); }
}
protected:
template <class T>
void add(T* t)
{
void* address = t;
mDerived[typeid(t).name()] = address;
if (mMostDerived == 0 || mMostDerived > address) { mMostDerived= address; }
}
private:
typedef std::map < char const*, void* > derived_t;
void* mMostDerived;
derived_t mDeriveds;
};
// Purposely no doing anything to help swapping...
template <class T>
T* dynamiccast(Object* o) { return o ? o->dynamiccast<T>() : 0; }
template <class T>
T const* dynamiccast(Object const* o) { return o ? o->dynamiccast<T>() : 0; }
template <class T>
T& dynamiccast(Object& o)
{
if (T* t = o.dynamiccast<T>()) { return t; }
else { throw std::bad_cast(); }
}
template <class T>
T const& dynamiccast(Object const& o)
{
if (T const* t = o.dynamiccast<T>()) { return t; }
else { throw std::bad_cast(); }
}
你需要一些小東西在構造函數中:
class Base: public Object
{
public:
Base() { this->add(this); }
};
所以,讓我們檢查:
- 經典用途:沒關係
virtual
繼承?它應該工作...但沒有測試- 尊重訪問說明... ARG:/
好運任何人試圖實現這個編譯器之外,真正做到:X
在頂層的類佈局的圖形描述中,請將「Byte」更改爲其他內容,因爲vtable-pointer [幾乎肯定]不是Byte。 – iAdjunct 2016-10-13 14:50:50
@iAdjunct:的確,我用'Cell'代替。 – 2016-10-13 15:02:07
簡單。使用虛函數WhoAmI()從某個類型化接口派生所有對象。 在所有派生類中重寫它。
怎麼樣上傳? – 2010-07-04 08:07:45
ONe的方式是聲明一個靜態標識符(例如整數),它定義了類ID。在該類中,您可以實現靜態和範圍例程,它將返回類標識符(記住標記例程虛擬)。
靜態標識符應在應用程序初始化時初始化。一種方法是爲每個類調用InitializeId例程,但這意味着必須知道類名,並且每次修改類層次結構時都應修改初始化代碼。另一種方法是在構建時檢查有效標識符,但是這會引入開銷,因爲每次構建類時都會執行檢查,但只有第一次是有用的(此外,如果沒有構建類,則不能使用靜態例程因爲標識符從未初始化)。
一個公平的實現可能是一個模板類:
template <typename T>
class ClassId<T>
{
public:
static int GetClassId() { return (sClassId); }
virtual int GetClassId() const { return (sClassId); }
template<typename U> static void StateDerivation() {
gClassMap[ClassId<T>::GetClassId()].push_back(ClassId<U>::GetClassId());
}
template<typename U> const U DynamicCast() const {
std::map<int, std::list<int>>::const_iterator it = gClassMap.find(ClassId<T>); // Base class type, with relative derivations declared with StateDerivation()
int id = ClassId<U>::GetClassId();
if (id == ClassId<T>::GetClassId()) return (static_cast<U>(this));
while (it != gClassMap.end()) {
for (std::list<int>::const_iterator = pit->second.begin(), pite = it->second->end(); pit != pite; pit++) {
if ((*pit) == id) return (static_cast<U>(this));
// ... For each derived element, iterate over the stated derivations.
// Easy to implement with a recursive function, better if using a std::stack to avoid recursion.
}
}
return (null);
}
private:
static int sClassId;
}
#define CLASS_IMP(klass) static int ClassId<klass>::sClassId = gClassId++;
// Global scope variables
static int gClassId = 0;
static std::map<int, std::list<int>> gClassMap;
CLASS_IMP應當從每一個CLASSID類派生來定義,gClassId和gClassMap應在全局範圍內可見。
可用的類標識符由所有類(基類ClassID或全局變量)可訪問的單個靜態整數變量保存,每次分配新的類標識符時該變量都會增加。
爲了表示類層次結構,在類標識符及其派生類之間的映射就足夠了。要知道是否可以將某個類轉換爲特定的類,請遍歷該地圖並檢查聲明派生。
面對使用參考文獻有很多困難!虛擬派生!不好的鑄造!錯誤的類型映射初始化將導致鑄造錯誤...
類之間的關係應手動定義,用初始化例程硬編碼。這允許確定一個類是否派生自,或者是否兩個類作爲一個通用派生。
class Base : ClassId<Base> { }
#define CLASS_IMP(Base);
class Derived : public Base, public ClassId<Derived> { }
#define CLASS_IMP(Derived);
class DerivedDerived : public Derived, public ClassId<DerivedDerived> { }
#define CLASS_IMP(DerivedDerived);
static void DeclareDerivations()
{
ClassId<Base>::StateDerivation<Derived>();
ClassId<Derived>::StateDerivation<DerivedDerived>();
}
我個人同意 「還有,如果編譯器實現dynamic_cast的理由」;編譯器可能會更好地執行這些操作(特別是在示例代碼方面!)。
假設你已經發現你有相同層次結構中的類的引用,你如何提出實際執行'dynamic_cast'? – 2010-07-04 08:57:05
已更新。我認爲這顯然是解決方案的道路。 – Luca 2010-07-04 20:30:13
作爲'DynamicCast'的結果,你似乎使用'ClassId'專用化的'reinterpret_cast'。當然,這是錯誤的課程,即使它是正確的課程,reinterpret_cast也不會對MI層次做適當的調整。我錯過了解決方案中顯而易見的事情嗎? – 2010-07-05 07:27:33
要試圖回答略少常規,如果略少定義:
你需要做的是轉換指針爲int *什麼,在棧上創建一個新的類型T,將指針給它int *,並比較兩種類型中的第一個int。這將做一個vtable地址比較。如果它們是相同的類型,它們將具有相同的vtable。否則,他們不。
我們更明智的只是在課堂上堅持積分常數。
但是這並不能告訴你是否有可能在兩種類型之間使用'dynamic_cast'或者如何執行轉換。 – 2010-07-04 09:09:06
- 1. 如何編寫自己的isnumber()函數?
- 2. 如何編寫自己的Android LocationService?
- 3. 如何編寫自己的日誌類
- 4. 如何編寫我自己的ContextMenu? C#
- 5. 如何編寫我自己的AuthorizeTag?
- 6. 如何編寫自己的Webstorm插件?
- 7. 如何編寫自己的CasperJS模塊?
- 8. 如何編寫我自己的loop_until?
- 9. 編寫我自己的BIOS
- 10. JIT編譯器 - 如何開始編寫自己的編譯器?
- 11. 如何在Prolog中編寫/編輯自己的協程?
- 12. 如何編寫自己的自定義GKMatchMakerViewController?
- 13. 如何編寫自定義/自己的離子框架插件
- 14. 編寫你自己的Jquery函數?
- 15. 編寫我自己的float解析器
- 16. 編寫我自己的keras圖層
- 17. 編寫自己的oh-my-zsh插件
- 18. C++編寫自己的輸入操縱
- 19. 編寫你自己的圖層
- 20. 找不到自己編寫的腳本
- 21. Wicket:編寫自己的組件
- 22. 編寫我自己的拆分方法
- 23. 在C中編寫我自己的shell
- 24. 編寫我自己的塊方法
- 25. 編寫自己的HTTP服務器
- 26. 雄辯的ORM - 如何編寫我自己的查詢?
- 27. 如何編寫打印自己的後綴的嵌套宏?
- 28. 如何編寫自己的.net混淆器
- 29. 如何在qt中編寫我自己的集合?
- 30. 如何爲Crawler4J編寫自己的異常處理?
@ saurabh01返回通過你提出的問題,選擇最有用的答案(如果有的話)。點擊該答案左邊的複選框即可接受。謝謝! – 2010-07-04 08:31:03
絕對是這個月最尷尬的面試問題的有力競爭者。 – 2010-07-04 08:38:00
@尼爾巴特沃斯:我同意。編譯器實現DC是有原因的。 – Puppy 2010-07-04 08:45:32