2010-09-27 112 views
0

在C++ for Windows中,我有一些對象工廠應該通過將指向對象的指針傳遞給Create函數並返回創建的對象來創建一系列Info對象。將參數指針返回給對象

void CreateInfoObject(AbstractInfo** info); // The creation function 

AbstractInfo是一個基類,我們有許多類型的Info對象派生。

我認爲如下我現在可以創建一個信息對象:

MyInfoObject* InfoObj = NULL; // derived from AbstractInfo object 
InfoFactory fc; 

fc.CreateInfoObject(&InfoObj); // Now I want to get my initialized pointer back 

但它說,它不能做投...什麼是錯的?

錯誤: 無法從MyInfoObject ** _ W64轉換爲AbstractInfo **

編輯:第一個答案中提到,該接口是可怕的,看不出誰的分配等等...我怎麼能提高?

回答

8

讓我們想想一個可能實現的CreateInfoObject

void InfoFactory::CreateInfoObject(AbstractInfo** info) 
{ 
    *info = new SuperInfo; 
} 

現在,SuperInfoMyInfoObject不具備共同的權利什麼?

這就是爲什麼,在一般情況下,以下是被禁止的:

struct Base {}; 
struct D1: Base {}; 
struct D2: Base {}; 

int main(int argc, char* argv[]) 
{ 
    Base** base = nullptr; 
    D1* d = nullptr; 
    base = d; 
} 

因爲這將使D1指向的東西無關。

有幾種解決方案:

// 1. Simple 
AbstractInfo* info = nullptr; 
fc.CreateInfoObject(info); 

// 2. Better interface 
std::unique_ptr<AbstractInfo> info = fc.CreateInfoObject(); 

然後,如果你知道肯定地說,你,其實,一個MyInfoObject你可以使用:

MyInfoObject* myInfo = static_cast<MyInfoObject*>(info); 

,或者如果您不確定:

MyInfoObject* myInfo = dynamic_cast<MyInfoObject*>(info); 

如果有的話,它將設置myInfonullptrinfo未指向MyInfoObject(或派生)的實例。

但請記住,您的界面真的很可怕。它非常C-ISH,不清楚記憶是否真的被分配......以及誰負責處理它,如果是的話。

編輯

良好 C++風格,我們使用RAII雙方表示所有權和確保清理。 RAII是衆所周知的,雖然不是很具有說服力,但我自己更喜歡新的SBRM(Scope Bound Resources Management)。

的想法是,而不是使用裸指針,這並不表明任何所有權(即你必須調用刪除就可以了?),你應該使用智能指針,例如像unique_ptr

您也可以使用方法的返回參數,以避免有兩個步驟的初始化過程(首先創建指針,然後使其指向一個對象)。這裏有一個簡明的例子:

typedef std::unique_ptr<AbstractInfo> AbstractInfoPtr; 

// Note: if you know it returns a MyInfoObject 
// you might as well return std::unique_ptr<MyInfoObject> 
AbstractInfoPtr InfoFactory::CreateInfoObject() 
{ 
    return AbstractInfoPtr(new MyInfoObject()); 
} 

// Usage: 
int main(int argc, char* argv[]) 
{ 
    InfoFactory factory; 
    AbstractInfoPtr info = factory.CreateInfoObject(); 

    // do something 

} // info goes out of scope, calling `delete` on its pointee 

在這裏,關於所有權沒有歧義。

此外,請注意你如何更好地理解你的問題在這裏:

std::unique_ptr<MyInfoObject> info = factory.CreateInfoObject(); 

不會編譯,因爲你不能轉換AbstractInfo*MyInfoObject*,而無需使用static_castdynamic_cast

+0

編輯討論您的評論! – 2010-09-27 21:10:18

+1

@Tony:我編輯了我的答案,提供了一個替代接口的提案。 – 2010-09-28 06:28:24

+0

非常感謝您的解釋。現在我還可以從檢索到的AbstractInfoPtr中進行下拉式轉換爲派生的對象嗎?這是一個好主意嗎? – 2010-09-28 07:25:38

2

指向指針的指針並不像指向對象的指針那麼靈活。編譯器將嚴格執行該類型而不考慮繼承樹。

爲了解決這個問題最安全的方法是使用雙重任務:

MyInfoObject* InfoObj = NULL; // derived from AbstractInfo object 
AbstractInfo* temp = NULL; 
InfoFactory fc; 

fc.CreateInfoObject(&temp); 
InfoObj = dynamic_cast<MyInfoObject*>(temp); 
+0

即使他沒有使用任何指針返回一個對象(例如使用'AbstractInfo InfoFactory :: CreateInfoObject();'。)方法,問題就會存在。)他正試圖將一個抽象類轉換爲其具體子類,從而產生病態形成的程序,不會編譯。 – 2010-09-27 16:12:39

+0

@Jonathan,這個問題正是我在答案中使用'dynamic_cast'的原因。如果返回的對象實際上不是'MyInfoObject',則投射將失敗。 – 2010-09-27 17:23:29

3

因爲CreateInfoObject()需要一個指針到一個指針到一個AbstractInfo,這是可能的函數返回一個實例AbstractInfo不是MyInfoObject的一個實例。所以你最終可能會得到一個指向MyInfoObject的指針,即實際上是指向一個DifferentInfoObject

更改MyInfoObject *InfoObjAbstractInfo *InfoObj它應該工作。除了dynamic_cast<>之外,不要拋棄轉換,因爲您不確定CreateInfoObject()是否會返回該子類的實例。

3

編譯器告訴你什麼是錯的。當T和U彼此無關時,不能將T *類型的指針轉​​換爲U *類型的指針。在這裏,T = MyInfoObject *,U = AbstractInfo *,這兩個不同的指針類型不共享任何繼承關係。

+1

問題不在於指針。問題是'CreateInfoObject()'可能返回'AbstractInfo'的實例,它不是* MyInfoObject的一個實例,調用代碼無法確保不會發生。 – 2010-09-27 16:14:02

+0

爲了強化這個答案:即使類MyInfoObject和AbstractInfo具有繼承關係,指針類型MyInfoObject *和AbstractInfo也不會。 – 2010-09-27 17:39:21

+1

@Jonathan:調用代碼無論如何是無效的。它是無效的,因爲T和U不是參考相關的。編譯器不會讓你做這樣的事情,除非你明確地轉換了指針,在這種情況下你可能遇到你指的問題。 – sellibitze 2010-09-27 17:40:08

0

考慮到發生在CreateInfoObject

假設有另一個AbstractInfo的子類別,請致電Foo

裏面CreateInfoObject我們創建一個新的Foo並將其分配給*info。 (允許upcast)。

但我們現在有FooMyInfoObject**裏面,這是錯誤的。