代码之家  ›  专栏  ›  技术社区  ›  rmeador

如何捕获初始化静态成员时引发的异常

  •  7
  • rmeador  · 技术社区  · 16 年前

    我有一个静态成员的类:

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

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

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

    5 回复  |  直到 16 年前
        1
  •  7
  •   Matthieu M.    16 年前

    我不喜欢 static 数据成员太多,初始化是首要问题。

    每当我必须进行重要的处理时,我就会作弊并使用本地

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

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

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

    • 如果你的编译器能处理它,那就好了
    • 你可以用 boost::once Boost.Threads 图书馆
    • 因为它是 ,您可能不关心它是否被多次初始化,除非 someOtherFunction 不支持并行执行(注意资源)

    指南 :仅使用 静止的 global 简单对象(不能抛出)的变量实例化,否则使用 local static 变量来延迟执行,直到可以捕获结果异常为止。

        2
  •  5
  •   Dan Olson    16 年前

        3
  •  5
  •   Jerry Coffin    16 年前

    当然可以——包起来 SomeFunction() 在如下函数中:

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

    static_error != 0 如果需要,打印一条适当的错误消息(很遗憾,您无法知道 std::cerr 在异常处理程序中仍然存在,因此如果要从那里打印,则必须执行类似于基于C文件的输出的操作。

        4
  •  0
  •   David Rodríguez - dribeas    16 年前

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

        5
  •  0
  •   Eld    16 年前

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

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