9

我有一个可以在其构造函数中引发异常的类。如何在 try/catch 块中声明该类的实例,同时仍使其在正确的范围内可用?

try { MyClass lMyObject; }
catch (const std::exception& e) { /* Handle constructor exception */ }

lMyObject.DoSomething(); // lMyObject not in scope!

在尊重RAII习语的同时,是否有其他方法可以做到这一点?

我不希望使用init()两阶段施工的方法。我唯一能想到的另一件事是:

MyClass* lMyObject;

try { lMyObject = new MyClass(); }
catch (const std::exception& e) { /* Handle constructor exception */ }

std::shared_ptr<MyClass> lMyObjectPtr(lMyObject);
lMyObjectPtr->DoSomething();

工作正常,但我对范围和指针间接中的原始指针不满意。这只是另一个 C++ 疣吗?

4

5 回答 5

5

如果构造函数抛出,则意味着对象无法初始化,因此无法开始存在。

MyClass* lMyObject;
try { lMyObject = new MyClass(); }
catch (std::exception e) { /* Handle constructor exception */ }

在上面如果构造函数抛出异常,lMyObject则保持未初始化,换句话说,指针包含一个不确定的值。

有关详细说明,请参阅经典的构造函数失败:

我们可以将 C++ 构造函数模型总结如下:

任何一个:

(a) 构造函数通过到达其结束或返回语句正常返回,并且对象存在。

或者:

(b) 构造函数通过发出异常退出,对象不仅现在不存在,而且从未存在过。

没有其他可能性。

于 2013-02-05T15:02:02.743 回答
2

编写代码的最佳方法是:-

MyClass lMyObject; 
lMyObject.DoSomething(); 

没有尝试、捕获或指针。

如果构造函数抛出,则 DoSomething 不能被调用。这是正确的:如果构造函数抛出,那么对象永远不会被构造。

而且,重要的是,不要捕获(甚至捕获/重新抛出)异常,除非您对它们有建设性的意义。让异常发挥它们的作用,直到知道如何处理它们的东西可以发挥作用为止。

于 2013-02-05T15:43:02.230 回答
1

Constructors are for putting an object into a consistent state and establishing class invariants. Allowing an exception to escape a constructor means that something in that constructor has failed to complete, so now the object is in some unknown weird state (which may include resource leaks now too). Since the object's constructor has not completed, the compiler will not cause it's destructor to be invoked either. Perhaps what you're looking for is to catch the exception inside the constructor. Assuming it's not rethrown, this would cause the constructor to complete execution, and the object is now fully-formed.

于 2013-02-05T15:34:09.547 回答
0

你不需要使用shared_ptr,使用unique_ptr

std::unique_ptr<MyClass> pMyObject;
try { pMyObject.reset(new MyClass()); }
catch (std::exception &e) { /* Handle constructor exception */ throw; }
MyClass &lMyObject = *pMyObject;

lMyObject.DoSomething();

显然,您有责任确保程序在catch没有初始化pMyObject或退出函数(例如 viareturnthrow)的情况下不会通过该块。

如果可用,您可以使用 Boost.Optional 来避免使用堆内存:

boost::optional<MyClass> oMyObject;
try { oMyObject.reset(MyClass()); }
catch (std::exception &e) { /* Handle constructor exception */ throw; }
MyClass &lMyObject = *oMyObject;

lMyObject.DoSomething();
于 2013-02-05T15:07:09.380 回答
-1

您可以设置 MyClass 的复制构造函数来接受垃圾输入,从而有效地使用您的对象声明声明一个指针。然后您可以在 try 块中手动调用默认构造函数:

MyClass lMyObject(null); // calls copy constructor
try {
    new (lMyObject) MyClass(); // calls normal constructor
}
catch (const std::exception& e) { /* Handle constructor exception */ }

lMyObject.DoSomething();
于 2013-02-05T15:23:20.693 回答