我正在使用C ++ Primer,第5版,并且作者提供了一个示例,使用shared_ptr
来管理可能泄漏内存的旧库中的资源,以防止它们这样做。我决定创建一个测试来查看它是如何工作的,但我的自定义删除器在抛出异常并且(故意)没有被捕获后也没有被调用:
#include <iostream>
#include <memory>
#include <string>
struct Connection {};
Connection* Connect(std::string host)
{
std::cout << "Connecting to " << host << std::endl;
return new Connection;
}
void Disconnect(Connection* connection)
{
std::cout << "Disconnected" << std::endl;
delete connection;
}
void EndConnection(Connection* connection)
{
std::cerr << "Calling disconnect." << std::endl << std::flush;
Disconnect(connection);
}
void AttemptLeak()
{
Connection* c = Connect("www.google.co.uk");
std::shared_ptr<Connection> connection(c, EndConnection);
// Intentionally let the exception bubble up.
throw;
}
int main()
{
AttemptLeak();
return 0;
}
它产生以下输出:
连接到www.google.co.uk
我的理解是,当一个函数退出时,无论是正常退出还是因为异常退出,局部变量都将被销毁。在这种情况下,这应该意味着connection
在AttemptLeaks()
退出时被销毁,调用其析构函数,然后调用EndConnection()
。另请注意,我正在使用和刷新cerr
,但这也没有给出任何输出。
我的例子或我的理解是否有问题?
修改:虽然我已经有了这个问题的答案,但对于将来偶然发现这个问题的其他人来说,我的问题在于我对throw
如何运作的理解。虽然以下答案正确说明了如何使用它,但我认为最好明确说明我(错误地)尝试使用它来生成&#39;一个未处理的异常,用于测试上面的代码。
答案 0 :(得分:8)
Bare throw
旨在用于catch块内部以重新抛出捕获的异常。如果您在catch块之外使用它,将调用terminate()
并且您的程序立即结束。见what does "throw;" outside a catch block do?
如果删除throw
- 语句,则shared_ptr
connection
将超出范围并应调用删除者。如果您对使用shared_ptr的异常安全性有任何疑问(我不知道),您可以通过将throw
更改为throw 1
来明确抛出异常。
答案 1 :(得分:3)
没有操作数的throw
表达式用于重新抛出当前处理的异常。如果没有处理异常,则调用std::terminate
。在这种情况下,不会发生堆栈展开,这就是永远不会调用删除器的原因。将您的代码更改为以下内容:
void AttemptLeak()
{
Connection* c = Connect("www.google.co.uk");
std::shared_ptr<Connection> connection(c, EndConnection);
// Intentionally let the exception bubble up.
throw 42; // or preferably something defined in <stdexcept>
}
int main()
{
try {
AttemptLeak();
} catch(...) {
}
return 0;
}
现在,当shared_ptr
超出范围时,将调用删除器。