2014-04-30 60 views
20

我正在使用一個庫,它具有與其構造函數不同的init函數。我每次做一個新的實例我需要調用,例如:從現在開始,我可以創建一個變量_const嗎?

MyClass a; 
a.init(); 

由於initconst,這使我從創建const的情況下(我不能寫const MyClass a)。有什麼辦法可以調用init,然後從「here on out」聲明(我猜想其餘的範圍)我的變量是const

這工作,而是依賴於不接觸原始變量:

MyClass dont_touch; 
dont_touch.init(); 
const MyClass & a = dont_touch; 
+0

答案是否定的。像你一樣,我希望是的。 – Mehrdad

+0

你也可以使用'const_cast'作爲'init'調用來做到快速和骯髒。也就是說,在'init'調用的對象周圍聲明對象'const'和'const_cast'。然而,'const_cast'通常被認爲是不好的做法,我必須說我認爲[模板解決方案](http://stackoverflow.com/a/23400028/3100771)是超級光滑的。 – Apriori

+1

@Apriori絕對未定義的行爲。不能const_cast從一個已定義的const對象中取出const並且對它做非const的東西。 –

回答

16

如果您使用C++ 11,你可以使用lambda函數

const MyClass ConstantVal = []{ 
    MyClass a; 
    a.init(); 
    return a; 
}(); 

這可以讓你保持初始化到位而永不放棄的可變對象的外部訪問。 還看到: http://herbsutter.com/2013/04/05/complex-initialization-for-a-const-variable/

+1

我認爲這比我的解決方案更優化,因爲編譯器可以假定對象從未被修改過。 –

+0

這回答我的具體問題。儘管我可能會問一個更普遍的問題,其中'a.init()'被替換爲一些可能不適合lambda的任意操作。 –

5

創建一個包裝的前兩行,給你的對象是準備去的功能。

MyClass makeMyClass() 
{ 
    MyClass a; 
    a.init(); 
    return a; 
} 

// Now you can construct a const object or non-const object. 
const MyClass a = makeMyClass(); 
MyClass b = makeMyClass(); 

更新

使用makeMyClass()涉及臨時對象每次被調用的函數的構造和破壞。如果變成顯著成本,makeMyClass()可以改變:

MyClass const& makeMyClass() 
{ 
    static bool inited = false; 
    static MyClass a; 
    if (!inited) 
    { 
    inited = true; 
    a.init(); 
    } 
    return a; 
} 

它的使用,如前文所述,將繼續工作。另外,一次也可以這樣做:

const MyClass& c = makeMyClass(); 
+0

這是以複製爲代價的,是正確的? –

+1

是的,它確實是以犧牲拷貝爲代價的。 –

+3

@mangledorf我不確定這一點,但[返回值優化](https://en.wikipedia.org/wiki/Return_value_optimization)可能會有用。所以,如果我理解正確的話,如果你使用'-O3',我不認爲你需要擔心這個。只是一個想法。 – gongzhitaao

9

您可以創建一個包裝類並使用它。

如果MyClass的有虛析構函數,你可以從中感到安全推導如下:

class WrapperClass : public MyClass 
{ 
public: 
    WrapperClass() 
    { 
     init(); // Let's hope this function doesn't throw 
    } 
}; 

,或者編寫包含MyClass的實例

class WrapperClass 
{ 
public: 
    WrapperClass() 
    { 
     m_myClass.init(); // Let's hope this function doesn't throw 
    } 
    operator MyClass&() {return m_myClass;} 
    operator const MyClass&() const {return m_myClass;} 
private: 
    MyClass m_myClass; 
}; 

類或編寫模板來解決這個使用上述兩種解決方案之一的一般問題:例如,

template <class T> class WrapperClass : public T 
{ 
public: 
    WrapperClass() 
    { 
     T::init(); 
    } 
}; 

typedef WrapperClass<MyClass> WrapperClass; 
+0

可能值得注意的是,如果需要,可以在其使用的函數內部和局部定義這樣的派生類。但使用模板解決方案更好,避免這種情況,因爲您可以隨意實例化。 – Apriori

+1

'init'完全沒問題。我們希望它不會返回錯誤代碼。 init函數的通常原因是開發人員知道可能發生錯誤,但不知道異常,所以如果發生錯誤,他不能報告錯誤。 – MSalters

1

實際上,你可以做到這一點很簡單,即使沒有C++ 11 lambda表達式和:

const MyClass a; 
{ 
    MyClass _a; 
    _a.init(); 
    std::swap(const_cast<MyClass&>(a), _a); 
} 

採用const_cast是無可否認的一個黑客位,但它贏得了」 t破壞任何東西const是一個相當弱的說明符。同時,它非常高效,因爲MyClass對象只能被交換,不能被複制(最合理的複製對象應該提供swap函數並注入過載std::swap)。

不投,它需要一個幫手:

struct Construct_Init { 
    operator MyClass() const 
    { 
     MyClass a; 
     a.init(); 
     return a; 
    } 
}; 
const MyClass a = Construct_Init(); 

這可就是這樣一個功能(Construct_Init結構需要沒有命名空間範圍進行聲明),但它是一個有點長。對象的副本可能會或可能不會使用copy elision優化。

請注意,在這兩種情況下,返回值init()都會丟失。如果它返回一個布爾值,其中true是成功和false是失敗的,最好是:

if(!a.init()) 
    throw std::runtime_error("MyClass init failed"); 

或者只是確保妥善處理錯誤。

相關問題