【问题标题】:C++ shared_ptr inheritance memory leakC++ shared_ptr 继承内存泄漏
【发布时间】:2011-10-04 18:26:43
【问题描述】:

我有一个 shared_ptr 指向子类基础的情况。

当shared_ptr去删除指针时,只有父析构函数被调用。

父母析构函数是虚拟的,孩子不是,尽管我已经尝试过所有组合。

我在 valgrind 中有该程序,它显示在创建对象时在 new 语句中创建了内存。而且我知道父析构函数被调用,但子析构函数没有。

这是孩子:

class NetworkUserAgent : public bbs::UserAgent
{
friend class Server;

public:
    NetworkUserAgent(boost::asio::io_service &ioService, size_t _szBuffer=512u);
    ~NetworkUserAgent();

    void asyncRead();
    void doneRead(std::shared_ptr< std::vector<char> > pBuf,
                    const boost::system::error_code &error, size_t byTrans);

    void writeTo(const std::string &msg);
    void doneWrite(const boost::system::error_code &error, size_t byTrans);

void close();

private:
    boost::asio::ip::tcp::socket socket_;
    const size_t szBuffer;
};

父母:

class UserAgent
{
public:
    //'structors
    UserAgent();
    virtual ~UserAgent();

    //commication
    virtual void writeTo(const std::string &msg)=0;
    std::function<void(std::string&)> dataRead;

    //user management
    void login(AccessLevel _accessLevel, int userId, const std::string &_userName);
    void logout();

    //Accessors
    AccessLevel UserAccessLevel() const;
    const std::string &UserName() const;
    const int &UserId() const;
    bool LoggedIn() const;

    //shared to allow reference to child type
    std::shared_ptr<ContextAgentData> contextAgentData;
private:
    std::string userName;
    int userId;

    AccessLevel accessLevel;
};

用法:

void Server::reset()
{   
    shared_ptr<NetworkUserAgent> client (new NetworkUserAgent(ioService));
    acceptor_.async_accept(client->socket_,
        [=] (const boost::system::error_code &error)
            { this->clientAccepted(client, error); }
        );
}

void Server::clientAccepted(shared_ptr<NetworkUserAgent> client,
                                const boost::system::error_code &error)
{
    if(error) return;
    cout << "[] New client has connected" << endl;

    //Generalise to Network useragent
    shared_ptr<UserAgent> uaClientPtr=client;
    context->receiveUserAgent(uaClientPtr);
    client->asyncRead();
    reset();
}

The rest of the code can be seen here.

谢谢。

另外请注意,上面的代码仍在进行中。

编辑:我错了,正在调用子析构函数,

NetworkUserAgent::~NetworkUserAgent()
{   
    this->close();
} 

void NetworkUserAgent::close()
{
    if(!socket_.is_open()) return; //socket is already closed
    //one or more of these functions are probably redundant  
    cout << "send request" <<endl;
    socket_.shutdown(ip::tcp::socket::shutdown_send);
    cout << "cancel" <<endl;
    socket_.cancel();
    cout <<"close"<<endl;
    socket_.close();
    cout << "done" <<endl;
}

编辑: 我做了更多的测试,恐怕问题比我希望的要复杂。销毁项目时会调用析构函数,但是问题是一旦 UserAgent 进入系统,它就不会被销毁。有些东西正在停止被破坏。

如果它与用户代理的 shared_ptr 的几个容器不同,当一个包含被销毁时,是否调用了内部元素的析构函数?

请让我知道我还能提供什么来解决问题。

【问题讨论】:

  • 从问题中发布的代码中不清楚何时应该删除共享指针。我查看了您链接中的代码(顺便说一句,这是不受欢迎的),那里有太多内容需要处理。您能否详细说明您希望何时删除共享指针?你怎么确定孩子的析构函数没有被调用(它不会像父母那样记录任何东西)?
  • 一个简单的例子只是为了展示std::shared_ptr正确处理这种情况:ideone.com/TRXVu。我必须支持 Chad 的问题,即你有多确定没有调用正确的析构函数?
  • 请提供重现问题的可编译运行代码。很有可能在您省略的代码中的某处发生切片。
  • n.m 我会尝试,但问题很大。如果您查看链接,我相信您可以轻松下载 src 文件夹并制作它。 (如果你点击快照就可以了)谢谢
  • 您的扩展问题听起来像您有一个悬空的参考。有人持有shared_ptr 的时间比预期的要长,因此该对象永远不会被删除。这很容易由循环引用引起...a 持有b,它持有c,它持有a,因此即使没有外部引用a,它们也不会被删除。考虑在不需要实际所有权的地方使用weak_ptr。此外,您的 lambda 表达式可能会存储在路径中的某个位置,并且它们也会保存副本。

标签: c++ inheritance c++11 memory-leaks shared-ptr


【解决方案1】:

UserAgent 中有一个 dataRead std::function,它最终被设置为一个包含 std::shared_ptr 的 lambda,以阻止它自毁。我添加了一个 close() 方法并将 std::function 设置为默认值。

现在一切都很好,删除也很好

感谢您的所有帮助

【讨论】:

    【解决方案2】:

    我怀疑您的对象正在被切片,尽管我没有要测试的环境。

    尝试以下方法之一:

    shared_ptr<UserAgent> uaClientPtr = boost::static_pointer_cast<UserAgent>(client);
    shared_ptr<UserAgent> uaClientPtr = boost::dynamic_pointer_cast<UserAgent>(client);
    

    【讨论】:

    • 这会更安全,但在这里是不必要的,因为已知这两种类型可以相互继承。不应发生切片。
    【解决方案3】:

    void Server::reset() auto_ptr 就足够了。

    在第二种情况下,我会做以下事情:

    void Server::clientAccepted(shared_ptr<NetworkUserAgent> client, const boost::system::error_code error)
    {
      if(error) return;
      cout << "[] New client has connected" << endl;
      context->receiveUserAgent(client);
      client->asyncRead();
      this->reset();
    }
    

    【讨论】:

    • 这更整洁,但问题仍然存在
    猜你喜欢
    • 2020-06-23
    • 2011-02-26
    • 1970-01-01
    • 2011-02-13
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 2014-04-01
    相关资源
    最近更新 更多