如何捕捉抛出的异常,同时初始化静态成员
我有一个静态成员的类:如何捕捉抛出的异常,同时初始化静态成员
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
不支持并行执行(提防资源)
指南:仅使用简单对象static
或global
变量实例(不能丢),否则使用local static
变量来延迟执行,直到您可以捕获所产生的异常。
也许最好的办法是将注册表键添加到列表中,而不是查看它,然后只要输入main(),就会查看列表中的所有键。我不想讲道,但是像这样的情况正是为什么在进入main()之前进行重要处理通常是个坏主意。
这个问题是,我的对象是一个常量...它必须在那个时候初始化。除非我使用const_cast ... – rmeador 2010-02-24 23:08:30
没错,我忽略了这一点。你当然可以制作全局对象指针并在main中初始化它们,但这可能涉及很多代码更改。在这一点上它是学术性的......人们已经在其他答案中提出了一个实用的解决方法,我唯一的建议是如何避免pre-main()行为。然而,消除这种行为可能不是必要的(尽管它也可以避免其他问题)。 – 2010-02-25 00:04:13
@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 *的输出)。
@Jerry,根据C++标准(whee,我喜欢这样说)27.4小节2,静态对象的构造函数和析构函数可以访问iostream全局对象。 – MSN 2010-02-25 03:44:14
@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
只是引起它的const并不意味着你不关心它被多次初始化。它可以访问一个你真正只想要击中一次的资源。 SomeOtherFunction也可能需要很长时间才能运行。无论如何,在main运行之前,你可能不希望它执行。 – Eld 2010-02-26 05:12:03
的确,我只是想指出可能不需要担心同步:'const'意味着它在初始化后永远不会改变,而如果不是(例如想到一个计数器),那么在重新设置之后被使用会把事情搞砸。当然,这取决于OP来减轻手头上的风险......我将编辑答案,以使最后一点更加清晰。 – 2010-02-26 07:16:05
我很确定我的代码没有任何线程问题,它应该在任何额外线程启动之前进行初始化。我已经实施了这个解决方案,它似乎解决了这个问题。谢谢! – rmeador 2010-02-26 15:54:13