C++基础异常问题

4

有人能否描述一下处理以下情况的正确方式:

wchar_t* buffer = new wchar_t[...];

if (!something)
{
    throw std::runtime_error("Whatever");
    // Now, at this point I'm leaking memory allocated for the 'buffer'.
}

// Perform actions.
delete[] buffer;

明显的解决方法类似于:
if (!something)
{
    delete[] buffer;
    throw std::runtime_error("Whatever");
}

现在 - 没问题吧?(我猜应该是,但谁知道呢 :)


附言 我确实意识到有更好的方法 - 使用boost::scoped_array或者只使用std::wstring,这样析构函数会被调用从而释放分配的内存,只是好奇。

5个回答

8

您的洞察力是正确的。该模式

Acquire some resource
Do something
Release resource

从根本上讲,这是错误的,因为Do something可能会抛出异常并泄漏资源。此外,您必须记住释放资源,这是一个错误的源泉。

正如您所指出的那样,正确的方法是始终使用一个析构函数释放资源的对象。在C++中,这被称为RAII

这意味着例如永远不要在析构函数之外使用delete,或者永远不要依赖手动关闭文件句柄,永远不要手动解锁互斥锁等。学习智能指针,并在可以使用它们时使用它们。

请注意,一些语言(不包括C++)提供了一个finally关键字,它允许你执行一组指令,无论是否抛出异常。C++使用RAII,如果你编写正确的析构函数,就不必担心资源释放。
我有一个小实用程序there,适用于C++0x,可以在块退出时执行任意代码,如果你需要与编写不良(或C)库进行一两次接口,则非常有用。

+1,总是使用std::auto_ptr、boost::shared_ptr / std::unique_ptr std::shared_ptr(都是C++11)或类似的自动删除。 - tauran
我不同意RAII意味着“永远不要手动关闭文件句柄,永远不要手动解锁互斥锁”等说法。RAII表明您不必手动执行此操作,因为保证会为您完成。但是,在某些情况下,您可能希望手动执行它(例如,当您需要检查关闭失败的结果时)。"永远"是一个强烈的词语。 - ToddR
1
@ToddR:即使您确实想尽快关闭文件或检查失败原因,您仍然可以将RAII作为安全网,如果您没有到达手动关闭文件的点,它会自动为您关闭文件。尽管如此,我还是改变了措辞以反映我们的讨论。 - Alexandre C.

0

在哪里有 catch?如果它在同一范围内 - 则可以在那里处理 delete,否则您的 #2 是正确的选择。当然,这是假设你不想像你自己在 PS 中提到的那样做到正确...


0

这取决于“something”是什么。如果计算中的something可能会抛出异常,那么不,这并不好。您必须小心处理每个获取的资源,可以通过使用堆栈分配变量(在作用域结束时被销毁)或使用各种智能指针(std库、boost等)来实现。


0
正确的方法是使用RAII模式。而不是使用原始指针,您应该将其包装在一个对象中,在其析构函数中处理释放内存,例如std::wstringstd::unique_ptr

0

如果您明确使用未包装的 new,则必须明确使用 delete。因此,在这种情况下,您必须捕获异常,然后调用 delete。

正如您所说,正确的方法是将缓冲区包装在一个 C++ 类中,其析构函数将被调用。对于简单的缓冲区,具有最小开销的类可能是 std::vector,但智能指针(例如 boost::scoped_ptr)也可以工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接