2009-06-18 77 views
10

我確定這是一個非常簡單的問題。下面的代碼顯示了我想要做的事:有條件地初始化C++成員變量的正確方法?

class MemberClass { 
public: 
    MemberClass(int abc){ } 
}; 

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) { 
     if(xyz == 42) 
      m_class = MemberClass(12); 
     else 
      m_class = MemberClass(32); 
    } 
}; 

這並不編譯,因爲m_class正在用一個空的構造(不存在)創建。這樣做的正確方法是什麼?我的猜測是使用指針並使用new實例化m_class,但我希望有一種更簡單的方法。

編輯:我應該早些說過,但我的實際問題有一個額外的複雜:我需要在初始化m_class之前調用一個方法,以便設置環境。所以:

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) { 
     do_something(); // this must happen before m_class is created 
     if(xyz == 42) 
      m_class = MemberClass(12); 
     else 
      m_class = MemberClass(32); 
    } 
}; 

是否有可能實現這個花哨的初始化列表技巧?

回答

24

使用條件運算符。如果表達式較大,使用函數

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(xyz == 42 ? 12 : 32) { 

    } 
}; 

class MyClass { 
    static int classInit(int n) { ... } 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(classInit(xyz)) { 

    } 
}; 

要初始化m_class之前調用的函數,你可以把一個struct該成員,並利用RAII

class MyClass { 
    static int classInit(int n) { ... } 
    struct EnvironmentInitializer { 
     EnvironmentInitializer() { 
      do_something(); 
     } 
    } env_initializer; 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(classInit(xyz)) { 

    } 
}; 

這之前會調用do_something()初始化m_class之前。請注意,在構造函數初始化程序列表完成之前,不允許調用MyClass的非靜態成員函數。該函數必須是其基類的成員,並且必須已經完成基類的工作。

另請注意,對於創建的每個獨立對象,不僅對於創建的第一個對象,當然總是調用該函數。如果你想這樣做,你可以在初始化程序的構造函數中創建一個靜態變量:

class MyClass { 
    static int classInit(int n) { ... } 
    struct EnvironmentInitializer { 
     EnvironmentInitializer() { 
      static int only_once = (do_something(), 0); 
     } 
    } env_initializer; 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(classInit(xyz)) { 

    } 
}; 

它使用逗號運算符。請注意,您可以通過使用捕獲由do_something拋出的異常功能try塊

class MyClass { 
    static int classInit(int n) { ... } 
    struct EnvironmentInitializer { 
     EnvironmentInitializer() { 
      static int only_once = (do_something(), 0); 
     } 
    } env_initializer; 
public: 
    MemberClass m_class; 
    MyClass(int xyz) try : m_class(classInit(xyz)) { 

    } catch(...) { /* handle exception */ } 
}; 

do_something功能將再次下一次調用,如果把它扔到導致該MyClass對象不能被創建例外。希望這有助於:)

+0

謝謝!我更新了我的問題 - 我實際上需要在創建m_class之前運行一個方法。那可能嗎?也許我可以在「classInit」中做到這一點,但那不會很優雅。 – 2009-06-18 19:11:01

+0

瞭解了很多,謝謝! :-) – 2009-06-18 19:42:02

5

使用初始化列表語法:

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(xyz == 42 ? MemberClass(12) : MemberClass(32) 
           /* see the comments, cleaner as xyz == 42 ? 12 : 32*/) 
    { } 
}; 

與工廠大概清潔:

MemberClass create_member(int x){ 
    if(xyz == 42) 
    return MemberClass(12); 
    // ... 
} 

//... 
MyClass(int xyz) : m_class(create_member(xyz)) 
+2

您正在創建一個未命名的對象,然後使用copy-ctor初始化成員,而不是直接初始化它。 – 2009-06-18 18:58:55

+0

是的,打字太快,爲我自己的好。就我個人而言,我仍然將邏輯移入MemberClass或工廠。 – 2009-06-18 19:00:05

5
MyClass(int xyz) : m_class(xyz==42 ? 12 : 32) {} 

要回答你的修訂問題,即變得有點棘手。最簡單的方法是將m_class作爲指針。如果你真的想要它作爲數據成員,那麼你必須有創意。創建一個新類(如果它是在MyClass內部定義的,最好)。是否需要調用函數?包括它第一個之間的數據成員的聲明(這將使其成爲第一個實例)。

class MyClass 
{ 
    class initer { public: initer() { 
        // this must happen before m_class is created 
        do_something();       
        } 
        } 

    initer  dummy; 
public: 

    MemberClass m_class; 
    MyClass(int xyz) : m_class(xyz==42? 12 : 43) 
    { 
     // dummy silently default ctor'ed before m_class. 
    } 
}; 
0

或者:

class MemberClass { 
public: 
    MemberClass(int abc){ } 
}; 

class MyClass { 
public: 
    MemberClass* m_class; 
    MyClass(int xyz) { 
     if(xyz == 42) 
      m_class = new MemberClass(12); 
     else 
      m_class = new MemberClass(32); 
    } 
}; 

如果你不知何故仍然要保持相同的語法。會員創新雖然效率更高。

0

試試這個:

class MemberClass 
{ 
public:  
    MemberClass(int abc = 0){ } 
}; 

這使得它的默認值,並且您的默認構造函數。

0

要讓初始化發生其他的事情發生後,你確實需要使用指針,這樣的事情:

class MyClass { 
public: 
    MemberClass * m_pClass; 
    MyClass(int xyz) { 
     do_something(); // this must happen before m_class is created 
     if(xyz == 42) 
      m_pClass = new MemberClass(12); 
     else 
      m_pClass = new MemberClass(32); 
    } 
}; 

唯一的區別是,你需要訪問成員變量m_pClass->counter代替m_class.counterdelete m_pClass在析構函數中。