如何在初始化静态成员时捕获抛出的异常

时间:2010-02-24 22:27:07

标签: c++ exception-handling static

我有一个带有静态成员的类:

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

我在CPP文件中初始化如下:

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

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

5 个答案:

答案 0 :(得分:7)

我不太喜欢static数据成员,初始化问题最重要。

每当我必须进行重要处理时,我会欺骗并使用本地static代替:

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

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

这样,异常只会在第一次使用时抛出,但对象将是const

这是延迟执行的非常强大的习惯用语。它有一点开销(基本上编译器每次进入方法时都会检查一个标志),但最好先担心正确性;)

如果从多个线程调用它:

  • 如果您的编译器处理它,那么
  • 如果您的编译器没有,您可以使用本地线程存储(无论如何都是const)
  • 您可以在boost::once
  • 中使用Boost.Threads
  • 因为它是const,你可能不在乎它是否多次初始化,除非someOtherFunction不支持并行执行(小心资源)

指南:仅对简单对象(不能抛出)使用staticglobal变量实例化,否则使用local static变量来延迟执行,直到可以抓住产生的例外情况。

答案 1 :(得分:5)

也许最好的办法是将注册表项添加到列表而不是查找它,然后只要输入main(),就可以查看列表中的所有键。我不想说教,但是这样的情况正是为什么在进入main()之前进行重要处理通常是个坏主意。

答案 2 :(得分:5)

当然 - 将SomeFunction()包裹在如下函数中:

int static_error;

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

然后在进入main时,您需要检查static_error != 0并在需要时打印相应的错误消息(遗憾的是,您无法知道异常处理程序中是否存在std::cerr,所以如果你想从那里打印,你将不得不做类似C FILE *的输出。

答案 3 :(得分:0)

您可以将该函数包装在另一个捕获异常的函数中,并向用户发出问题警告(或创建具有安全默认值的密钥)

答案 4 :(得分: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