错误处理是 C++ 构造函数中的一个挑战。有几种常见的方法,但它们都有明显的缺点。例如,抛出异常可能会导致构造函数中更早分配的资源泄漏,使其成为一种容易出错的方法。使用静态init()
方法是另一种常见的解决方案,但它违反了 RAII 原则。
研究这个主题,我发现这个答案和博客建议使用名为 的 C++17 功能std::optional<>
,我发现它很有希望。然而,这种解决方案似乎有一个潜在的问题——当用户检索到对象时,它会立即触发析构函数。
这是一个描述问题的简单代码示例,我的代码基于上述来源
class A
{
public:
A(int myNum);
~A();
static std::optional<A> make(int myNum);
bool isBuf() { return _buf; };
private:
char* _buf;
};
std::optional<A> A::make(int myNum)
{
std::cout << "A::make()\n";
if (myNum < 8)
return {};
return A(myNum);
}
A::A(int myNum)
{
std::cout << "A()\n";
_buf = new char[myNum];
}
A::~A()
{
std::cout << "~A()\n";
delete[]_buf;
}
int main()
{
if (std::optional<A> a = A::make(42))
{
if (a->isBuf())
std::cout << "OK\n";
else
std::cout << "NOT OK\n";
std::cout << "if() finished\n";
}
std::cout << "main finished\n";
}
该程序的输出将是:
A::make()
A()
~A()
OK
if() finished
~A()
a->_buf
尝试删除两次时出现运行时错误(至少在 Visual C++ 环境中) 。
cout
为了读者的方便,我发现这个问题调试了一个非常复杂的代码,但问题很清楚 - 中的语句return
构造A::make()
对象,但由于它是A::make()
范围的结尾 - 调用了析构函数。用户确定他的对象已初始化(注意我们是如何得到"OK"
消息的),而实际上它已被销毁,并且当我们超出if()
范围时main
,a->~A()
再次调用。
那么,我做错了吗?在构造函数中使用std::optional
for 错误处理很常见,至少有人告诉过我。提前致谢