如何捕获构造函数异常
How to catch a constructor exception?
我有一个C++类,它在失败时从构造函数抛出异常。如何分配此类的本地实例(不使用new
)并处理任何可能的异常,同时保持try
块范围尽可能小?
从本质上讲,我正在寻找与以下Java习语等价的C++:
boolean foo() {
Bar x;
try {
x = new Bar();
} catch (Exception e) {
return false;
}
x.doSomething();
return true;
}
我不想从x.doSomething()
捕获异常,只捕获构造函数。
我想我正在寻找一种方法来分离x
的声明和初始化。
是否可以在不使用堆分配和指针的情况下实现这一点?
您可以使用C++17:中的std::optional
bool foo() {
std::optional<Bar> x; // x contains nothing; no Bar constructed
try {
x.emplace(); // construct Bar (via default constructor)
} catch (const Exception& e) {
return false;
}
x->doSomething(); // call Bar::doSomething() via x (also (*x).doSomething() )
return true;
}
这个Java习惯用法不能很好地转换为C++,因为Bar x;
将需要默认构造函数,即使您的实际构造函数需要传递参数。
我建议在这种程度上与语言作斗争-扩大try
块就足够了-但如果真的想缩小范围,那么可以使用一个函数,并依靠返回值优化来避免值复制:
Bar foobar()
{
try {
return Bar();
} catch (Exception& e){
/* Do something here like throwing a specific construction exception
which you intercept at the call site.*/
}
}
但实际上,您可以在构造上抛出一个特定的异常,从而完全避免这种函数方法。
是的,如果您将所有代码都放在try
子句中,例如通过使用函数try块(以避免不必要的嵌套和作用域),这是可能的:
bool foo() try
{
Bar x;
x.doSomething();
return true;
}
catch (std::exception const& e)
{
return false;
}
或者在try
子句中调用另一个真正起作用的函数:
void real_foo()
{
Bar x;
x.doSomething();
}
bool foo() try
{
real_foo();
return true;
}
catch (std::exception const& e)
{
return false;
}
请注意,在构造函数中抛出异常通常不是一个好主意,因为这会停止对象的构造,并且不会调用其析构函数。
正如Holt所指出的,这也将捕获来自doSomething
调用的异常。有两种解决方法:
简单而标准的方法:使用指针。
使用两阶段构造:有一个不能抛出异常的默认构造函数,然后调用一个可以抛出异常的特殊"构造"函数。
第二种方法在C++标准化之前很常见,并在Symbian系统的代码中广泛使用。这种情况已经不常见了,因为使用指针更容易、更简单,尤其是在今天有了好的智能指针的情况下。我真的不推荐现代C++中的第二种方法。
当然,最简单的方法是确保构造函数根本不会抛出异常,或者如果抛出了异常,那么它们的性质是程序无论如何都无法继续,并终止程序。正如在对您的问题的评论中所指出的,C++中的异常是昂贵的,然后我们还有被放弃的构造问题,并且在所有情况下,C++中使用异常只能在特殊情况下进行。C++不是Java,即使两种语言中有相似的结构,也不应该这样对待它。
如果你仍然想从构造函数抛出异常,实际上有第三种方法可以只捕获这些异常:使用上面的一个代码示例,只抛出doSomething
永远不能抛出的特定异常,然后只捕获这些特定的构造函数。
通常,如果您想避免堆分配,则不能将局部变量的声明与其定义分开。因此,如果要将所有内容组合到一个函数中,则必须使用try/catch
块包围x
的整个范围:
boolean foo() {
try {
Bar x;
x.doSomething();
} catch (Exception e) {
return false;
}
return true;
}
编号。从您的java示例中,您将不得不在以下两种可能性之间进行选择:
无指针:
bool foo() {
try {
Bar x;
x.doSomething();
} catch (Exception e) {
return false;
}
return true;
}
带指针:
bool foo() {
Bar* x = nullptr;
try {
x = new Bar();
} catch (Exception e) {
return false;
}
x->doSomething();
delete x; // don't forget to free memory
return true;
}
或者使用托管指针:
#include <memory>
bool foo() {
std::unique_ptr<Bar> x;
try {
x = new Bar(); // until C++14
x = std::make_unique<Bar>(); // since C++14
} catch (Exception e) {
return false;
}
x->doSomething();
return true;
}
您必须在的变体之间进行选择
bool foo() {
std::unique_ptr<Bar> x;
try {
x = std::make_unique<Bar>();
} catch (const BarConstructorException& e) {
return false;
}
x->doSomething();
return true;
}
或
bool foo() {
try {
Bar x;
x.doSomething();
} catch (const BarConstructorException& e) {
return false;
}
return true;
}
在修订后的问题中,OP增加了的要求
"我不想从
x.doSomething()
捕获异常,只想捕获[局部变量]的构造函数。
翻译Java代码的简单方法
boolean foo() {
Bar x;
try {
x = new Bar();
} catch (Exception e) {
return false;
}
x.doSomething();
return true;
}
…对于C++,则使用Optional_
类(如Barton Nackmann Fallible
、boost::optional
或C++17 std::optional
)
auto foo()
-> bool
{
Optional_<Bar> xo;
try
{
xo.emplace();
}
catch( ... )
{
return false;
}
Bar& x = *xo;
// Possibly other code here, then:
x.doSomething();
return true;
}
一个很好的替代方案是重构代码,如下所示:
struct Something_failure {};
void do_something( Bar& o )
{
// Possibly other code here, then:
o.doSomething();
}
auto foo()
-> bool
{
try
{
Bar x;
do_something( x );
return true;
}
catch( Something_failure const& )
{
throw;
}
catch( ... )
{}
return false;
}
如果您不喜欢上面的方法,那么您可以始终使用动态分配的Bar
实例,例如使用std::unique_ptr
来保证清理,但是这具有动态分配的一般开销。在Java中,大多数对象都是动态分配的,所以这似乎不是一个严重的缺点。但在C++中,大多数对象都是超高速堆栈分配的,因此与普通操作相比,动态分配是一个非常慢的操作,因此必须权衡动态分配可能在概念上的简单性。
- 从构造函数抛出异常时如何克服内存泄漏
- 如何编写带有异常的构造函数
- 从 C++ 中异常的构造函数引发异常
- 我正在尝试创建一个使用 c++ 中的参数包构造函数的异常类
- 稍后在构造函数中重新启动异常指令删除此指令
- 自定义异常中的用户定义的空构造函数,具有多个继承和抽象基类
- 构造函数中引发的异常
- 如何捕获源自静态分配对象的构造函数的异常?
- 从构造函数内存泄漏引发异常
- C++:如何捕获构造函数引发的异常?
- std::vector::emplace() 真的在面对抛出移动构造函数/赋值运算符时提供了强大的异常保证吗?
- V8 - 节点C++插件 - 在构造函数中引发异常会导致"致命错误:v8::ToLocalChecked Em
- 为什么要抛出引用调用复制构造函数的异常?
- std::vector 默认构造函数可以抛出异常吗?
- 如何防止构造函数在引发异常时创建对象
- 当构造函数的参数类型错误时引发异常
- 异常构造函数中的例外
- C++11 引入了采用 'const char*' 的异常构造函数.但是为什么
- 在异常构造函数中参数化错误消息是一种好的做法吗
- 异常构造函数来填充字符串流