2009-11-12 52 views
5

我最近遇到了使用配置對象而不是通常的setter方法進行配置的類。一個小例子:配置結構vs設置器

class A { 
    int a, b; 
public: 
    A(const AConfiguration& conf) { a = conf.a; b = conf.b; } 
}; 

struct AConfiguration { int a, b; }; 

有利的一面:

  • 你可以擴展你的對象,並輕鬆地保證新值合理的默認值,而用戶永遠需要去了解它。
  • 您可以檢查配置是否一致(例如,您的類只允許某些值的組合)
  • 您可以通過省略setter來節省大量代碼。
  • 您將獲得一個默認構造函數,用於指定Configuration結構的默認構造函數並使用A(const AConfiguration& conf = AConfiguration())

缺點(S):

  • 你需要知道在施工時間的配置和不能改變它以後。

是否有更多的缺點,我錯過了?如果沒有:爲什麼這不是更頻繁使用?

回答

6

無論你通過數據單獨或每結構是一個風格問題,需要根據具體情況逐案進行決定。

重要的問題是:對象在構建之後是否準備好和可用,並且編譯器是否強制將所有必要的數據傳遞給構造函數,或者是否必須記得在構造之後調用一組setter在沒有編譯器的情況下隨時增加您需要修改代碼的任何提示。因此,這是否是

A(const AConfiguration& conf) : a(conf.a), b(conf.b) {} 

A(int a_, int b_) : a(a_), b(b_) {} 

無所謂所有的東西。 (有大量的參數,每個人都寧願是前者,但這是哪個號碼 - 以及是否這樣一類是精心設計 - 是值得商榷的。)不過,我是否可以使用該對象這樣

A a1(Configuration(42,42)); 
A a2 = Configuration(4711,4711); 
A a3(7,7); 

或要做到這一點

A urgh; 
urgh.setA(13); 
urgh.setB(13); 

之前,我可以使用該對象,並使一個巨大的差異。特別是如此,當有人出現並將另一個數據字段添加到A時。

1

使用這種方法使二進制兼容性更難。

如果更改了結構(添加了一個新的可選字段),則使用該類的所有代碼可能都需要重新編譯。如果添加了一個新的非虛擬setter函數,則不需要重新編譯。

+0

我最近遇到了一箇舊的代碼庫,我支持。這很煩人。 – 2009-11-12 16:23:12

+0

Windows API通過添加一個大小字段,確保結構是POD,並且只在最後附加數據字段來做到這一點。添加支持舊的構造函數來創建新的結構(新字段具有合理的默認值),兼容性實際上是_increased_。 – sbi 2009-11-12 16:31:52

+0

我寫了一個鏡像答案,在那裏我顯示兼容性增加了。 – 2009-11-12 16:33:43

2

主要優點是A對象可以是不可變的。我不知道AConfiguration stuct actualy是否給構造函數的a和b參數帶來任何好處。

4

使用此方法可使二進制兼容性更容易。

當庫版本的變更,如果配置struct包含它,然後構造可以區分「老」或「新」的配置是否通過和訪問不存在的領域時,避免出現「訪問衝突」 /「段錯誤」。

此外,構造函數的損壞名稱被保留,如果它改變了它的簽名,它會發生改變。這也讓我們保持二進制兼容性。

例子:

//version 1 
struct AConfiguration { int version; int a; AConfiguration(): version(1) {} }; 
//version 2 
struct AConfiguration { int version; int a, b; AConfiguration(): version(2) {} }; 

class A { 
    A(const AConfiguration& conf) { 
    switch (conf.version){ 
     case 1: a = conf.a; b = 0; // No access violation for old callers! 
     break; 
     case 2: a = conf.a; b = conf.b; // New callers do have b member 
     break; 
    } 
    } 
}; 
0

我會支持這裏降低的二進制兼容性。

我看到的問題來自直接訪問struct字段。

struct AConfig1 { int a; int b; }; 
struct AConfig2 { int a; std::map<int,int> b; } 

因爲我修改的b表示,我擰,而用:

class AConfig1 { public: int getA() const; int getB() const; /* */ }; 
class AConfig2 { public: int getA() const; int getB(int key = 0) const; /* */ }; 

對象的物理佈局可能有變化,但我的干將沒有和偏移功能也沒有。

當然,對於二進制兼容性,應該檢查出PIMPL成語。

namespace details { class AConfigurationImpl; } 

class AConfiguration { 
public: 
    int getA() const; 
    int getB() const; 
private: 
    AConfigurationImpl* m_impl; 
}; 

雖然你寫更多的代碼,你在這裏你的對象的向後兼容性有保證,只要你加入現有的後補充方法。

在存儲器不依賴於的方法的數目的實例的表示,它僅取決於:

  • 虛擬方法的存在與否
  • 基類
  • 屬性

這是什麼是可見的(不是什麼是可訪問的)。

這裏我們保證我們不會有任何屬性變化。 AConfigurationImpl的定義可能沒有任何問題地改變,並且方法的實現也可能改變。

更多的代碼意味着:構造函數,複製構造函數,賦值運算符和析構函數,這是一個相當數量,當然還有getter和setter。另請注意,這些方法不能再內聯,因爲它們的實現是在源文件中定義的。

無論它是否適合您,您都可以自行決定。