2011-04-27 47 views
2

我有一個類,其所有的默認構造函數都超出了我的控制範圍。我需要避免在某些情況下而不是在其他情況下構建它們。有什麼我可以做到這一點?C++避免構造一個對象

的模式是這樣的:

class A 
{ 
public: 
    //ctor + dtor + methods 
    A(); 
    ~A(); 
    // Data members beyond my control 
    X1 x1; 
    X2 x2; 
    ... 
    XN xn; 
}; 

class B 
{ 
public: 
    B() 
    {} 

    // Data members 
    A a; // This instance of A is the issue 
}; 

的問題是X1XN完全超出了我的控制。類A只是一個包裝x1xn在一起的類。我想有條件地在B中創建A的實例。現在的問題是X1XN的默認構造函數在某些情況下需要避免,而不是在其他情況下。所以無論我是否構造AB都會嘗試創建一個使用默認構造函數A,這將繼而啓動默認構造函數X1XN。這是我需要避免的。

我目前正在使用宏來解決這個問題,但想知道是否有更好的解決方案。

+2

這個問題太含糊。情況如何?就其本身而言:只需使用指針即可。 – 2011-04-27 15:05:25

+8

如果您不想創建類的實例,請不要編寫用於創建類實例的代碼。 – 2011-04-27 15:06:01

+0

您可以將默認構造函數定義爲private。看看C++中的Singleton模式,看看如何做到這一點。 – Kieveli 2011-04-27 15:34:43

回答

1

您可能需要使用可空類型一樣的boost ::可選。這看起來像:

class B { 
public: 
    B() 
    { 
     /* 
     * Here, a has been default constructed and is empty 
     * You can do computations here and then... 
     */ 
     if(/* some elaborate condition*/) { 
      a = A(); 
      /* access *a from here on */ 
     } else { 
      /* anything you want */ 
     } 
    } 

private: 
    boost::optional<A> a; 
}; 

這回答這個問題,但我認爲,如果你告訴你真正想實現一個更合適的答案可以給予。我覺得這更像是一個設計問題,而不是語言問題。擴展的想法如下。

在上面的'解決方案'中,else子句中出現了什麼?由於A顯然只能是默認構造的,所以不像你可以放置不同的構造函數調用。如果您不初始化a,則會引入(圈)複雜度,因爲這意味着每個方法都必須檢查a是否處於活動狀態。或者你可以扔;然而,我會重構在函數中執行檢查(或其他)的代碼;一個私人靜態方法或如下所示的匿名/靜態獨立功能:

namespace { 
A 
prepare_A() 
{ 
    /* elaborate computations, possibly throw */ 
    return A(); 
    /* 
    * if A had different constructors we could also conditionally 
    * return an A(0) or whatever. 
    */ 
} 
} // namespace 

B::B() 
: 
    a(prepare_A()) 
{ 
    /* 
    * a is of type A, not boost::optional<A> 
    */ 
} 

然而,這假設是A能夠複製。如果情況並非如此,或者複製A是不可接受的,我會認爲第一種解決方案是可接受的,因爲作爲類不變量a的一部分永遠不會爲空。

如果我們知道AB之間的關係,推薦一些東西會更容易。如果你想有條件地初始化它,你爲什麼要把A成員放入B?或許兩者之間的關係,首選應該不會聚集,但聯想:一個指針成員(或引用成員,如果你小心賦值運算符),以A如:

class B { 
public: 
    explicit 
    B(A& a_) 
    /* 
    * Important: not A const&, 
    * we only want lvalues. 
    * Contract on the caller: a must remain valid during 
    * the lifetime of *this. 
    */ 
    : 
     a(&a_) 
    { 
     /* 
     * class invariants: a != 0, 
     * *a remains valid for the lifetime of B 
     */ 
    } 

private: 
    A* a; 
}; 

這樣,你不會引入圈複雜度而且你不在乎A和A何時構建。合同爲調用者增加了一點負擔,但由於我們只能將左值傳遞給構造函數,所以很難(呃)濫用。

+0

thnx爲長的答案。 – Egon 2011-05-01 02:57:55

4

正如魯道夫康拉德指出:

class B 
{ 
public: 
    B(bool create = false) 
    { 
    a = (create ? new A : NULL); 
    } 
    ~B() 
    { 
    delete a; 
    } 
private: 
    A* a; 
}; 
+0

此代碼適用於託管代碼。我希望儘可能地避免指針,因爲這些代碼將被移交給主要僅使用託管代碼/腳本編寫的團隊。 – Egon 2011-05-01 02:56:55

0

目前還不清楚你想避免哪個構造函數,但是在任何情況下你都可以使用聯合。

從Stroustrup的第4版:

如果聯合具有用戶定義的構造函數,複製操作,移動操作或析構函數的成員,則該特殊功能被刪除(第3.3節。 4,§17.6.4);也就是說,它不能用於聯合類型的對象。

所以,如果你想構造一個當它在B,使用:

class B { 
public: 
    B() 
    {} 

    // Data members 
    union { 
     A a; // This instance of A is the issue 
    }; 
};