2011-08-09 40 views
1

我需要抓住引用一個對象的引用,我用來做什麼的,像這樣:C++獲取到返回的對象

MyObject& obj = FactoryThatGivesAnObject(); 
obj.MethodThatModifieObj(); 

不,我需要做的是基於一個條件:

MyObject obj; 

// Need obj to be a reference to the returned values below 
if(foo) 
    obj = FactoryThatGivesAnObject(); 
else 
    obj = OtherFactoryThatGivesAnObject(); 

obj.MethodThatModifiesObj(); 

如何在第二個示例中將obj作爲參考?

+0

爲什麼你想要它是一個參考? – Beta

回答

3

一種解決方案可能是使用三元運算符:

obj = foo ? FactoryThatGivesAnObject() : OtherFactoryThatGivesAnObject(); 

你也可以使用一個指針:

MyObject* pobj; 
if(foo) 
    pobj = &FactoryThatGivesAnObject(); 
else 
    pobj = &OtherFactoryThatGivesAnObject(); 
+0

嗯..這裏有一個異常安全問題。爲什麼不''auto_ptr'或'unique_ptr'? –

+1

其實,它比這更糟糕。假設工廠按照價值返回東西,你已經創建了一個指向未命名臨時的指針(這顯然是不好的)。如果他們通過引用返回,那麼工廠需要維護對象的狀態... –

+1

我正在做與OP中的代碼示例相同的假設。 –

1

如何我已經OBJ是在第二個例子中的參考?

你不行。引用是別名;你只能通過指向某些東西來創建它們,一旦你指出它們,它們就不能被重新分配。

您可能會更喜歡在這裏使用類似std::auto_ptrstd::unique_ptr的東西。請注意,您的工廠需要返回auto/unique_ptr。如果你的工廠正在返回一個引用,我懷疑你可能會偶然地返回對未命名臨時對象的引用(未定義的行爲),但是沒有看到工廠的代碼就很難說清楚。

+0

爲什麼你認爲它們可能是對未命名的臨時對象的引用?例如,「工廠」很可能會返回對池中對象的引用。 –

+1

@Pierre:我假設,因爲99%的時間,我看到人們做這個返回引用期望的東西像Java或C#中的引用一樣工作。 –

+0

@Pierre:那*是*可能的,但是不太可能,OP非常歡迎用細節修改她的問題。 –

2

你的第一行是黑幕:

MyObject& obj = FactoryThatGivesAnObject(); 

如何是這樣運作的?工廠方法不能返回臨時對象的引用,所以它可能返回的唯一明智的引用是一個動態創建的對象 - 但現在由誰來負責這個對象?

(除非你只是恢復到現有的對象的引用,那就是但我假設你的工廠真正創建新的對象。)

此代碼是內存泄漏車禍;我看不出有什麼辦法可以寫出任何明智的東西。更好的方法是將新創建的對象返回到負責任的容器中,例如一個shared_ptrunique_ptr

#include <memory> 

std::unique_ptr<MyObject> FactoryFunction() 
{ 
    return std::unique_ptr<MyObject>(new MyObject(3,5,7)); 
} 

這樣一來,如果沒有人拿起廠家的產品,或如果發生異常,動態分配的對象將得到妥善處理。

這也可以輕鬆地將取決於條件分配不同的指針:

std::unique_ptr<MyObject> x; 

if (...)  { x = Factory1(); } 
else if (...) { x = Factory2(a,b); } 
else   { x = Factory3(argc, argv); } 
+0

+1好的'unique_ptr'示例。 –

+0

這些可以是擁有對象的類的成員,也可以是其他工廠通過某種池註冊新對象的情況。 –

+0

@Moo:是的,可以。 *可能。 OP歡迎詳細闡述。 –

5

參考,不像指針,只能設置一次。這是一個很有用的功能,但這是令人沮喪的一個方面。你只想設置參考一次,但可能是不同的事情。

您有兩種選擇。

1)使用三元運算

這往往是最簡單的,如果你只處理兩個工廠,一個簡單的布爾來決定使用哪一種:

MyObject& obj = (foo 
        ? FactoryThatGivesAnObject(); 
        : OtherFactoryThatGivesAnObject()); 

但是,如果foo更復雜,或者如果您有多個工廠選項,則下一個選項可能會更清晰。

2)使用自己的

MyObject& get_an_object(const int state) // or whatever parameters you need 
{ 
    switch(state) 
    { 
     case USE_LEGACY_FACTORY: return FactoryThatGivesAnObject(); 
     case USE_FOO_FACTORY:  return OtherFactoryThatGivesAnObject(); 
     case DO_SOMETHING_ELSE:  return YetAnotherObjectFactory(); 
    } 
    throw std::runtime_error("Bad Factory Selector"); 
} 

// usage is simpler now 
MyObject& obj = get_an_object(foo); 

請注意,您可能需要幾個參數傳遞給你的工廠方法的工廠方法:

  • 選擇標準。你的例子只是foo - 一個簡單的布爾值。隨着事情的發展,您可能需要額外的標準來幫助確定使用哪個工廠。
  • 工廠對象。您可能有工廠對象而不是工廠方法,在這種情況下,您需要將對這些對象的引用傳遞到您的方法中。
+0

+1,因爲即使沒有進入指針,它也可以工作。儘管我仍然擔心OP工廠的內部問題。 –

0

這裏有一個解決方案,它在技術上不是工廠,而是解決了同樣的問題 - 提供了新的對象,當您更改參數:

struct A 
{ 
    int a; 
    float x; 
    int c; 
}; 
class ObjectCollection 
{ 
public: 
    ObjectCollection() { m_a.c = 10; } 
    A &get_obj_a(int a, float x) 
    { 
    m_a.a = a; 
    m_a.x = x; 
    return m_a; 
    } 
private: 
    A m_a; 
}; 

這個版本的優點是,它不圍繞所有權傳遞給該對象,但您仍然可以使用它創建不同種類的對象。兩次調用get_obj_a()會導致問題,但只有在需要該對象之前立即調用get_obj_a()時纔有效。現在if語句可以放在工廠函數中。這裏還有另一種方法:

class DerivedFactory 
{ 
public: 
    DerivedFactory(ObjectCollection1 &c, ObjectCollection2 &c2) : c(c),c2(c2) { } 
    Base &get_obj_a_or_b(bool b) { 
     if (b) return c.get_obj_a(10,11.0); 
     else return c2.get_obj_b(20.0,13.0); 
    } 
private: 
    ObjectCollection1 &c; 
    ObjectCollection2 &c2; 
};