如何捕捉抛出的异常,同时初始化静态成员

问题描述:

我有一个静态成员的类:如何捕捉抛出的异常,同时初始化静态成员

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

与我在CPP初始化文件,像这样:

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

的问题是, SomeFunction()从注册表中读取一个值。如果该注册表项不存在,则会引发异常。这会导致我的程序爆炸而不给用户任何有用的输出......有什么方法可以捕获异常,以便我可以记录它?

我不喜欢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

也许最好的办法是将注册表键添加到列表中,而不是查看它,然后只要输入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

可以换行捕捉异常,并提醒该问题的用户(或创建了一个安全的默认值的键)

当然另一个函数内部的功能 - 像一个功能包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

你可以做一个包装类,延迟对象的构造。然后,当它的第一次使用时,如果构造函数抛出,它将抛出第一次使用它的地方。

这样做的好处是在调用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