2011-02-10 83 views
4

假設我有一類實施幾個接口爲什麼選擇static_cast而不是隱式轉換鏈?

class CMyClass : public IInterface1, public IInterface2 { }; 

並且在I類需要獲得void*指針這些接口中的一個(典型情況IUnknown::QueryInterface()的成員函數。

典型的解決方案是使用static_cast實現指針調整:

void* pointer = static_cast<IInterface2*>(this); 

,它是在這種情況下,安全的,如果沒有已知的CLAS從CMyClass繼承。但是,如果這樣的類存在:

class CDerivedClass : public CUnrelatedClass, public CMyClass {}; 

我accidentially做

void* pointer = static_cast<CDerivedClass*>(this); 

this實際上是指向CMyClass實例的編譯器不會趕上我,以後的程序可能會遇到不確定的行爲 - static_cast變得不安全。

建議的解決方案是使用隱式轉換:

IInterface2* interfacePointer = this; 
void* pointer = interfacePointer; 

看起來這將解決這兩個問題 - 指針調整和無效垂頭喪氣的風險。

第二種解決方案是否存在問題?有什麼可以選擇第一個的原因?

+1

它有趣的是`CMyClass`具有CDerivedClass`的`知識在這裏......不是不可能的,甚至不是一個真正可怕的設計的跡象,但在一般情況下,`CMyClass`不應該有任何的知識後人。我可以想象在頭文件中定義了這兩個類,在定義了`CMyClass`方法的翻譯單元中包含了這兩個類,在VS中它更傾向於提倡預編譯頭文件...仍然是值得思考的。 – 2011-02-10 09:13:58

回答

3

您可以使用此模板:

template<class T, class U> T* up_cast(U* p) { return p; } 

用法:

struct B {}; 
struct C : B {}; 

int main() 
{ 
    C c; 

    void* b = up_cast<B>(&c); 
} 

注意 '*' 是隱含的。如果您更喜歡up_cast<B*>,請相應地調整模板。

2

指定void *始終是不安全的。無論怎樣你寫它,你可以搞砸了 - 假設用戶試圖QI爲接口1,則既不以下的將是一個警告或錯誤:鑑於

Interface2* interfacePointer = this; 
void* pointer = interfacePointer; 

void* pointer = static_cast<Interface2*>(this); 

在一個很有可能無法訪問派生類定義的文件中,意外使用static_cast進行投射的風險很小,但我發現很多額外的努力都是爲了實際的安全性很低。

+0

對於兩條線而言,「很多額外的努力」似乎有點誇大其詞。 – Simone 2011-02-10 08:49:49

+0

我認爲你應該努力閱讀,而不是努力寫作。 – daramarak 2011-02-10 13:47:20

2

我看不出有什麼理由不使用後一種解決方案,除了這樣一個事實,即如果別人正在讀你的代碼,它將不會立即溝通你爲什麼使用這樣一個複雜的陳述(「爲什麼不是他只是使用static_cast?!?「),所以最好對它進行評論或使其意圖非常清晰。

+0

在其他答案中使用其中一種模板建議,可以實現安全和清晰。 – Sjoerd 2011-02-10 09:13:41

+1

我覺得它仍然缺少一些東西,但至少它更簡潔。 – Simone 2011-02-10 09:29:54

1

您的分析聽起來很合適。之所以不使用你的隱式的做法是不引人注目:

  • 稍微詳細
  • 離開變量
  • 的static_cast <>可以說是比較常見的,因此更有可能是顯而易見的其他開發人員,搜查遊逛對等
  • 在許多情況下,派生類的連聲明不會的基類函數的定義之前出現,所以有這種類型的錯誤沒有潛在
1

如果你害怕與static_cast不小心做一些事情,那麼我建議你將鑄造/接口指針獲取包裝成某些模板函數,例如像這樣:

template <typename Interface, typename SourceClass> 
void set_if_pointer (void * & p, SourceClass * c) 
{ 
    Interface * ifptr = c; 
    p = ifptr; 
} 

或者,使用dynamic_cast並檢查NULL指針值。

template <typename Interface, typename SourceClass> 
void set_if_pointer (void * & p, SourceClass * c) 
{ 
    p = dynamic_cast<Interface *>(c); 
} 
相關問題