2010-02-24 52 views
7

我有一個靜態成員的類:如何捕捉拋出的異常,同時初始化靜態成員

class MyClass 
{ 
public: 
    static const SomeOtherClass myVariable; 
}; 

與我在CPP初始化文件,像這樣:

const SomeOtherClass MyClass::myVariable(SomeFunction()); 

的問題是, SomeFunction()從註冊表中讀取一個值。如果該註冊表項不存在,則會引發異常。這會導致我的程序爆炸而不給用戶任何有用的輸出......有什麼方法可以捕獲異常,以便我可以記錄它?

回答

6

我不喜歡static數據成員多,初始化是最重要的問題。

每當我需要做的顯著處理,我欺騙和使用本地static代替:

class MyClass 
{ 
public: 
    static const SomeOtherClass& myVariable(); 
}; 

const SomeOtherClass& MyClass::myVariable() 
{ 
    static const SomeOtherClass MyVariable(someOtherFunction()); 
    return MyVariable; 
} 

這樣,例外只在第一次使用拋出,然而對象將是const

這是一個非常強大的延遲執行成語。它有一個小的開銷(基本上編譯器每次進入方法時,檢查一個標誌),但大約正確性好擔心第一;)

如果這是從多個線程調用:

  • 如果你的編譯器手柄它,精細
  • 如果你的編譯器不,您可以使用本地線程存儲(它的常量反正)
  • 你可以在Boost.Threads庫使用boost::once
  • ,因爲它是const,遊馬Ÿ不關心,如果它的初始化多次,除非someOtherFunction不支持並行執行(提防資源)

指南:僅使用簡單對象staticglobal變量實例(不能丟),否則使用local static變量來延遲執行,直到您可以捕獲所產生的異常。

+0

只是引起它的const並不意味着你不關心它被多次初始化。它可以訪問一個你真正只想要擊中一次的資源。 SomeOtherFunction也可能需要很長時間才能運行。無論如何,在main運行之前,你可能不希望它執行。 – Eld 2010-02-26 05:12:03

+0

的確,我只是想指出可能不需要擔心同步:'const'意味着它在初始化後永遠不會改變,而如果不是(例如想到一個計數器),那麼在重新設置之後被使用會把事情搞砸。當然,這取決於OP來減輕手頭上的風險......我將編輯答案,以使最後一點更加清晰。 – 2010-02-26 07:16:05

+0

我很確定我的代碼沒有任何線程問題,它應該在任何額外線程啓動之前進行初始化。我已經實施了這個解決方案,它似乎解決了這個問題。謝謝! – rmeador 2010-02-26 15:54:13

5

也許最好的辦法是將註冊表鍵添加到列表中,而不是查看它,然後只要輸入main(),就會查看列表中的所有鍵。我不想講道,但是像這樣的情況正是爲什麼在進入main()之前進行重要處理通常是個壞主意。

+0

這個問題是,我的對象是一個常量...它必須在那個時候初始化。除非我使用const_cast ... – rmeador 2010-02-24 23:08:30

+0

沒錯,我忽略了這一點。你當然可以製作全局對象指針並在main中初始化它們,但這可能涉及很多代碼更改。在這一點上它是學術性的......人們已經在其他答案中提出了一個實用的解決方法,我唯一的建議是如何避免pre-main()行爲。然而,消除這種行爲可能不是必要的(儘管它也可以避免其他問題)。 – 2010-02-25 00:04:13

+0

@MSN,對不起,我不確定你的意思... iostream與問題或答案有什麼關係? – 2010-02-25 01:27:50

0

可以換行捕捉異常,並提醒該問題的用戶(或創建了一個安全的默認值的鍵)

5

當然另一個函數內部的功能 - 像一個功能包SomeFunction()

int static_error; 

void SomeFunctionWrapper() { 
    try { 
     SomeFunction(); 
    } 
    catch(...) { // something more specific if possible 
     static_error = 1; 
    } 
} 

然後在進入主,你要檢查static_error != 0,如果需要打印相應的錯誤消息(不幸的是,你可以不知道,如果std::cerr在異常處理程序還不存在,所以如果你要打印從那裏,你將不得不做類似C FILE *的輸出)。

+0

@Jerry,根據C++標準(whee,我喜歡這樣說)27.4小節2,靜態對象的構造函數和析構函數可以訪問iostream全局對象。 – MSN 2010-02-25 03:44:14

+0

@MSN:(實際上是§27。** 3 **/2)。出於某種原因,我以前從未注意到該腳註。顯然意圖在那裏,但我沒有看到任何真正保證這一點的規範性語言。無論如何,你的觀點可能是對的:我上面的代碼太過於偏執。 – 2010-02-25 04:59:17

0

你可以做一個包裝類,延遲對象的構造。然後,當它的第一次使用時,如果構造函數拋出,它將拋出第一次使用它的地方。

這樣做的好處是在調用main()之前不會有大量代碼運行,並且如果您沒有實際使用全局對象,它將永遠不會被初始化。

代碼:

#include <boost/bind.hpp> 
#include <boost/function.hpp> 
#include <boost/scoped_ptr.hpp> 
#include <boost/thread/once.hpp> 
#include <iostream> 

const boost::once_flag DEFAULT_ONCE_FLAG = BOOST_ONCE_INIT; 
template <typename T> 
class DelayedConstruction { 
    public: 
    DelayedConstruction(boost::function<T* (void) > const & init = &DelayedConstruction::default_initializer) : 
    m_initializer(init), m_flag(DEFAULT_ONCE_FLAG) { } 

    T const & operator*() const { 
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this)) ; 
    if (! m_object) 
     throw std::runtime_error("Object could not be initialized") ; 
    return *m_object ; 
    } 
    T const * operator->() const { 
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this)) ; 
    if (! m_object) 
     throw std::runtime_error("Object could not be initialized") ; 
    return m_object.get() ; 
    } 
    static T* default_initializer() { return new T; } 
    private: 
    void initialize() const { 
    m_object.reset(m_initializer()) ; 
    } 
    boost::function<T* (void) > m_initializer ; 
    mutable boost::scoped_ptr<T> m_object ; 
    mutable boost::once_flag m_flag ; 
}; 

struct Foo { 
    Foo(int x = 0) : m_x(x) { 
    if (x == 1) throw std::runtime_error("Can't be 1") ; 
    } 
    int m_x ; 
} ; 

Foo* make_custom_foo() { 
    return new Foo(1) ; 
} 

DelayedConstruction< const Foo > g_myFoo ; 
DelayedConstruction< const Foo > g_anotherFoo(&::make_custom_foo) ; 

int main() { 

    try { 
    std::cout << "My Foo: " << g_myFoo->m_x << std::endl ; 
    std::cout << "Another Foo: " << g_anotherFoo->m_x << std::endl ; 
    } catch (std::runtime_error const & e) { 
    std::cout << "ERROR: " << e.what() << std::endl ; 
    } 

    return 0 ; 
} 

打印出:

My Foo: 0 
ERROR: Can't be 1