2012-11-29 85 views
4

最近我在看一個古老的可怕的一類... 長話短說它將有助於線程安全,如果一個成員是const。
但問題是成員是在ctor中初始化的,並且它是非平凡的構造。所以我正在考慮爲該成員創建一個特殊的init函數。這是一個很好的決定。 小超簡單的例子(真正的代碼充滿了條件句:),並嘗試-S) :在初始化程序列表中使用複雜函數來初始化const成員一個好的設計?

class ComplexInitList 
{ 
std::pair<double,double> init_pair(const int first, const int second) 
{ 
    if ((first == 0) || (second == 0)) 
     throw std::invalid_argument("div by 0"); 
    return std::make_pair(1.0/first, 1.0/second); 
} 
const std::pair<double,double> p; 
public: 
ComplexInitList(int a, int b):p(init_pair(a,b)) 
{ 
    std::cout << p.first << ", " << p.second << std::endl; 
} 
}; 
+0

成員是否爲const對於線程安全性沒有影響;唯一的問題是你是否修改它。 (但恕我直言,你的解決方案仍然有意義。) –

+0

如果它是const,你可以在沒有互斥量的情況下讀取它(在C++ 11中) – NoSenseEtAl

+0

@NoSenseEtAl:如果它是非const的,你可以在沒有互斥量的情況下讀取它,假設它是未修改。 'const'告訴編譯器幫助確保它沒有被修改。並且請注意,即使它是'const',如果該對象是在一個線程中創建,然後從不同的線程訪問的,則無法排序就無法讀取它。只是你只需要對一對ops進行排序(創建和讀取),而如果有對非const對象的寫入,則他們需要對讀取進行排序。 –

回答

4

這實際上是給這個問題一個共同的和接受的解決方案。如果可能的話,考慮使init函數成爲靜態的,以便對正在構建的對象(例如虛擬調用行爲)的操作的意外語義無法啓動。

1

要初始化常量數據成員,唯一的方法是初始化它在member initializer list

p是一個常量,所以它可以被初始化爲一個值,但它不能被賦值。從概念上講,在執行括號內的代碼 之前,調用構造函數會創建一個對象。因此calling ComplexInitList(int a, int b):p(init_pair(a,b))導致程序首先爲成員p分配空間。

然後程序流程輸入 括號並使用普通賦值將值放入分配的空間。所以,如果你想初始化一個const數據成員,你必須初始化它member initializer list

2

遲到了,但添加任何新的搜索engine'ers:

如果init_pair不會在其他地方使用,我發現一個lambda是解決這個問題最優雅的方法:

class ComplexInitList 
{ 
const std::pair<double,double> p; 

public: 
ComplexInitList(const int a, const int b) : 
    p([](const int a, const int b) { 
     if ((first == 0) || (second == 0)) { 
      throw std::invalid_argument("div by 0"); 
     } 

     return std::make_pair(1.0/first, 1.0/second); 
    }()) // <-- Note the extra '()' with immediately calls the lambda! 
{ 
    std::cout << p.first << ", " << p.second << std::endl; 
} 

}; 
相關問題