【问题标题】:std::shared_ptr: Custom deleter not being invokedstd::shared_ptr:未调用自定义删除器
【发布时间】:2014-03-12 02:17:51
【问题描述】:

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

#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 工作原理的理解。虽然下面的答案正确地说明了如何使用它,但我认为最好明确说明我(错误地)试图使用它来“生成”一个未处理的异常,以测试我上面的代码。

【问题讨论】:

  • 总是在某处捕获异常
  • @MooingDuck 我知道。正如问题所述,我故意没有捕捉到它,以查看它的行为如何,并且它的行为不像作者所说的那样。
  • 正如下面的回答所说,你对shared_ptr和析构函数有正确的理解,你理解的错误是exceptions
  • @MooingDuck 感谢您验证我的理解。感谢您的帮助。

标签: c++ c++11 shared-ptr


【解决方案1】:

Bare throw 用于在 catch 块内重新抛出捕获的异常。如果你在 catch 块之外使用它,terminate() 将被调用并且你的程序立即结束。见what does "throw;" outside a catch block do?

如果您删除 throw 语句,shared_ptr connection 将超出范围并应调用删除程序。如果您对使用 shared_ptr 的异常安全性有任何疑问(我没有;),您可以通过将 throw 更改为 throw 1 在此处显式抛出异常。

【讨论】:

  • @JohnH:我想你不明白。 throw 本身在 catch 块内用于重新抛出捕获的异常。你没有什么可以重新抛出的。
  • 您的代码中没有catchterminate() 突然结束程序,根本没有抛出任何期望。请阅读引用的堆栈溢出问题“在 catch 块之外‘抛出’做什么”。
  • 啊,我现在从链接的答案中明白你的意思了。 If throw; is executed when an exception is not active, it calls terminate() (§15.1/8). 可能就是这样。
  • @PeterG。关于未找到处理程序时发生的情况(第 15.3/9 节):“如果未找到匹配的处理程序,则调用函数 std::terminate();无论在调用 std::terminate( ) 是实现定义的”。因此,您不能以一种或另一种方式下注(除非通过阅读实现文档)。
  • @PeterG。我认为“没有匹配的处理程序案例”不适用于在没有活动异常时遇到的抛出。相关章节为 §15.1/9 和 §15.5.1/2。在这种情况下,堆栈展开将永远发生。
【解决方案2】:

不带操作数的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 超出范围时将调用删除器。

【讨论】:

  • 感谢我对彼得回答的评论的替代方案。这也有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-19
  • 1970-01-01
  • 2018-01-31
  • 2012-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多