2010-04-23 34 views
16

如果我有一個構造函數,其中有2個必需的參數和4個可選參數,我該如何避免編寫16個構造函數或者10個左右的構造函數,如果我使用默認參數我不喜歡它,因爲這是糟糕的自我文檔)?有沒有使用模板的習語或方法,我可以用它來減少繁瑣? (更容易維護?)避免可選參數的枯燥

+1

你確定你的班級沒有嘗試做太多嗎? – 2010-04-23 18:20:22

+0

@Johannes:他希望跳過說d的可能性而不強迫調用者知道其默認值。 – Danvil 2010-04-23 18:24:57

+2

@John:我可以看到你在看什麼,但參數可以是屬性。例如,我有一個GUI Button類,它具有很多屬性,如圖像,文本,大小,顏色等等,但我不覺得它「做得太多」。恕我直言,按鈕服務於一個非常具體的目的,很容易適應一個類,用戶將立即知道什麼課程完成。 – Kyle 2010-04-23 18:32:18

回答

34

您可能會感興趣Named Parameter Idiom

總而言之,創建一個類來保存要傳遞給構造函數的值。添加一個方法來設置每個值,並使每個方法在末尾執行return *this;。在你的類中有一個構造函數,它接受一個對這個新類的const引用。這可以像這樣使用:

class Person; 

class PersonOptions 
{ 
    friend class Person; 
    string name_; 
    int age_; 
    char gender_; 

public: 
    PersonOptions() : 
    age_(0), 
    gender_('U') 
    {} 

    PersonOptions& name(const string& n) { name_ = n; return *this; } 
    PersonOptions& age(int a) { age_ = a; return *this; } 
    PersonOptions& gender(char g) { gender_ = g; return *this; } 
}; 

class Person 
{ 
    string name_; 
    int age_; 
    char gender_; 

public: 
    Person(const PersonOptions& opts) : 
    name_(opts.name_), 
    age_(opts.age_), 
    gender_(opts.gender_) 
    {} 
}; 
Person p = PersonOptions().name("George").age(57).gender('M'); 
+0

你將如何在構造函數中完成這項工作?通過在構造方法中使用'return this'? – 2010-04-23 18:21:22

+1

顯然,你不能從構造函數中返回任何東西。但是您可以使用一個副本,例如'Foo f = Foo()。option1()。option2();'。鏈接中的示例使用單獨的類來進行實際創建。 – 2010-04-23 18:27:14

+2

@Robert重要的是要注意右側以'PersonOptions'開頭,而不是'Person'。這創建了一個大概是輕量級的對象,然後有許多方法調用來將屬性設置爲非默認值。然後有一個名爲'Person(const PersonOptions&)'和ta da的ctor。一個對象的實例化(例如'PersonOptions()')在當前上下文中保留「* this」,所以你可以立即使用.method()方法。儘管如此,每個方法仍然需要'return * this'才能鏈接。 – 2010-04-23 18:48:24

9

如果您製作了一個包含所有字段的參數對象,該怎麼辦?那麼你可以通過它,只需設置你需要的任何領域。有可能是該模式的名稱,不知道它是什麼,但...

UPDATE:

代碼可能看起來有點這樣的:

paramObj.x=1; 
paramObj.y=2; 
paramObj.z=3; 
paramObj.magic=true; 
... //set many other "parameters here" 

someObject myObject = new someObject(paramObj); 

someObject構造函數中,你可以爲尚未設置的事件設置默認值(如果是強制性的,則會產生錯誤)。

老實說,我不是這個解決方案的忠實粉絲,但是我已經使用過一兩次,因爲paramObj通過包含一組通常所有的數據都是合理的(所以我們可以將它用於超過只是構造函數),它比多個構造函數更好。我發現它很醜,但它工作,YMMV。

+2

...並且此參數對象將在其默認構造函數中設置默認值。 – Danvil 2010-04-23 18:22:45

+1

爲什麼這比僅僅構建原始對象和設置屬性更好? – 2010-04-23 18:25:05

+0

@Johannes:這不就是弗雷德拉森在他的回答中提到的命名參數成語嗎? – 2010-04-23 18:27:05

4

而現在,爲「加速有它的東西」的答案:

Boost Parameter Library似乎是一個很好的適合你的使用情況。

1

所有新的C++ 17

#include <optional> 

using optional_int = std::optional<int>; 

class foo { 
    int arg0, arg1; // required 
    int arg2, arg3; // optional 
    const int default_2 = -2; 
    const int default_3 = -3; 
public: 
    foo(int arg0, int arg1, optional_int opt0 = {}, optional_int opt1 = {}) 
     : arg0(arg0), arg1(arg1) 
     , arg2(opt0.value_or(default_2)) 
     , arg3(opt1.value_or(default_3)) 
    { } 

}; 

int main() { 
    foo bar(42, 43, {}, 45); // Take default for opt0 (arg2) 
    return 0; 
} 

我有一個三次樣條實現,它允許用戶可選地在任一左端,右端,或兩者指定一階導數。如果未指定導數,則實際的代碼通過假定二階導數爲零(即所謂的「自然樣條」)來計算一個導數。這是一個左端的片段。

// Calculate the second derivative at the left end point 
    if (!left_deriv.has_value()) { 
     ddy[0]=u[0]=0.0; // "Natural spline" 
    } else { 
     const real yP0 = left_deriv.value(); 
     ddy[0] = -0.5; 
     u[0]=(3.0/(x[1]-x[0]))*((y[1]-y[0])/(x[1]-x[0])-yP0); 
    }