std::shared_ptr:未调用自定义删除器

std::shared_ptr: Custom deleter not being invoked

本文关键字:自定义 删除 调用 ptr shared std      更新时间:2023-10-16

我正在阅读c++ Primer,第5版,作者提供了一个使用shared_ptr s来管理可能泄漏内存的旧库资源的示例,以防止它们这样做。我决定创建一个测试来看看它是如何工作的,但是我的自定义删除器在抛出异常之后不会被调用,并且(故意)没有被捕获:

#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

我的理解是,当一个函数退出时,无论是正常退出还是因为异常退出,局部变量都将被销毁。在这种情况下,这应该意味着connectionAttemptLeaks()退出时被销毁,调用其析构函数,然后调用EndConnection()。还请注意,我正在使用并刷新cerr,但这也没有给出任何输出。

是我的例子有问题,还是我的理解有问题?

编辑:虽然我已经有了这个问题的答案,但对于将来偶然发现这个问题的任何人,我的问题是我对throw如何工作的理解。虽然下面的答案正确地说明了如何使用它,但我认为最好明确地说明一下,我(错误地)试图使用它来"生成"一个未处理的异常,以测试上面的代码。

throw用于在catch块内重新抛出捕获的异常。如果你在catch块外使用它,terminate()将被调用,你的程序立即结束。看看"扔"了什么;外面一个接球块怎么办?

如果您删除throw -语句,shared_ptr connection将超出作用域,并且应该调用delete。如果您对使用shared_ptr的异常安全性有任何疑问(我没有;),您可以通过将throw更改为throw 1来显式抛出异常。

没有操作数的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超出作用域时,将调用删除器