2013-04-29 196 views
0

如果我有一個簡單的程序如下: -從函數返回多個類型

class Program 
{ 
    public: 
     my_func(void)     \\ What return type do I use here?? 
     { 
      switch (i)    \\ assume i comes from somewhere... 
      { 
       case 1: 
       { 
        Glue g; 
        g.init(); 
        return g; 
       } 
       case 2: 
       { 
        Wood w; 
        w.init(); 
        return w; 
       } 
       .... 
      } 
     } 
} 

誰能告訴我怎樣才能設置此函數的返回類型爲中的一個創建的對象開關盒?另外由於時間限制,我不能使用new/delete來創建對象,或者我只是返回一個void * ptr。交換機中創建的對象足夠大,因此可以在每種情況下創建它們並返回對它們的引用,而不會超出範圍?

+4

請一次只問一個問題。如果您有兩個問題,請創建兩個SO問題。 – cdhowie 2013-04-29 16:15:01

+0

屬於另一個問題的第二個問題是在'my_func_2()'中返回一個懸而未決的非const引用。你認爲這個參考文獻是指什麼*回到呼叫方?仍然在努力處理第一個問題的實際嘗試,這看起來像試圖使用模板來提供重載(對於看起來你試圖完成的任務而言,它也不正確)。 – WhozCraig 2013-04-29 16:20:34

+0

在網上搜索「C++ factory Example」。 – 2013-04-29 19:39:12

回答

1

關於第一個問題,這是很好的設計可以確定,從可能的類型的對象的返回派生的基類。例如,讓Glue和Wood來自名爲CarpenterCarpenterObject的類,您需要在其中指示該對象實際是什麼。

class Carpenter 
{ 

public: 

    static const int UNKNOWN = -1; //example only 
    static const int GLUE = 0; 
    static const int WOOD = 1; 

    virtual int Type() { return UNKNOWN; } 

    Carpenter* GetSomething(); 

}; 

class Glue: public Carpenter 
{ 
    virtual int Type() { return Carpenter::GLUE; } 
}; 

class Wood: public Carpenter 
{ 
    virtual int Type() { return Carpenter::WOOD; } 
}; 

Carpenter* Carpenter::GetSomething() 
{ 
    Glue* g = new Glue(); 
    Wood* w = new Wood(); 

    return g; // or w 
} 

void main() 
{ 
    Carpenter p; 
    cout<<p.GetSomething()->Type(); 
} 

我覺得第二個問題涉及的警告不是錯誤(這與我正常工作),但我使用Visual Studio 2012的編譯器。

+0

你的第一句話是理想的答案(和我已經有的東西,但我的頭腦顯然在例外hanlder中拋出異常!) 我與你的答案唯一的問題是,我不能用new來創建該對象,或者它會很容易。我必須在每個case語句中創建對象(因爲每個case都會創建一個不同的對象),然後返回該對象。 (或者如你所建議的,基礎對象) – 2013-04-30 01:15:01

0

第一個節目模板的概念應用不當

,你必須通過類型調用函數時更換噸。

第二套方案我編譯我的編譯器,它在警告了兩者的功能

+0

對不起,刪除了問題的第二部分,因爲有人抱怨我在一個問中提出了兩個問題!是的,我知道使用模板將類型傳遞給函數,它更多的是從函數中獲取未知類型。 – 2013-04-30 01:20:49

2

第一個問題可以通過使用第二個問題的辦法來解決。這就是使GlueWood從相同的基類繼承,並傳遞給函數的引用。這樣你不必返回一個局部變量的引用,你可以返回你想要的類型。

struct Obj { 

    virtual void init() { cout << "Obj"; } 
}; 

struct Wood : public Obj { 
    virtual void init() { cout << "Wood"; } 
}; 


struct Glue : public Obj { 
    virtual void init() { cout << "Glue"; } 
}; 

struct Program { 

    // Pass by reference the actual object. You want it created it here? 
    // Why just create it outside. i.e move your case to the place where you know what i 
    // is. It doesn't have to be created here. 
    void foo(Obj& o) { 
     o.init(); 
    } 
}; 



int main() 
{ 
    Program p; 
    Obj* ref_p; 

    // Move your case here. 
    //case 
     Wood w; 
     ref_p = &w; 
    // 
     //Glue g; 
     //ref_p = &g; 

    p.foo(*ref_p); 
} 
+0

這是一個gppd的想法,但我的確有點簡化了代碼,以便於輸入等。在我的實際程序中,代碼必須是一個函數,根據執行的情況創建並返回創建的對象。每種情況都會使用不同的成員等創建不同的對象,併爲每個對象使用一個通用的init()函數。 從所有對象派生出來的基類的返回是我實際上已經得到的東西,但我的思想顯然是在雲 - 煮食地!將開關移到某個我知道它是什麼對象的地方,並不是一個真正的選擇。 – 2013-04-30 01:09:30

3

第一個問題的一個解決方案很明顯:獲得一個層次並返回一個基類型。另一種解決方案是使用Variant類型,如Boost.Variant。然後你可以定義你的返回類型爲:boost::variant<Wood, Glue>。當層次結構不自然或會增加複雜性時,這通常是更好的方法。另一方面,層次結構方法執行起來更簡單,有些人不會避開Variant類帶來的模板magick。

請注意,滾動自己的變體類型可能是一項相當複雜的任務,並且更喜歡圖書館。

你的第二個問題歸結爲:得到一個更好的編譯器。最近版本的clang和gcc 4.8都報告了這兩個函數的警告。

+0

+1 boost :: variant在這裏是一個有趣的sol'n – Mzn 2013-04-30 14:52:04