【问题标题】:C++11, shared_ptr.reset() and cyclic referencesC++11、shared_ptr.reset() 和循环引用
【发布时间】:2015-02-17 11:12:50
【问题描述】:

我对 shared_ptr.reset() 的行为有疑问。

在这种情况下,我有以下类的循环引用。我有一本书和一个所有者,它们都有 std::shared_ptrs 彼此,创建一个循环引用。

书.h

#pragma once

class Owner;

class Book
{
public:
    Book(std::string title);
    ~Book();
    void OutputDetails();
    void SetOwner(std::shared_ptr<Owner> owner);
    void OutputOwnerInformation();
private:
    std::string m_title;
    std::shared_ptr<Owner> m_owner; // Book hangs onto the owner and creates a circular dependency
};

Book.cpp

#include "stdafx.h"
#include <iostream>
#include "Book.h"
#include "Owner.h"

Book::Book(std::string title) : m_title(title) {}

Book::~Book() {
    std::cout << "Book Destroyed" << std::endl;
}

void Book::SetOwner(std::shared_ptr<Owner> owner) {
    m_owner = owner; // strong reference
}

void Book::OutputOwnerInformation() {
    std::cout << "Owner is: " << m_owner->GetName() << std::endl;
}

void Book::OutputOwnerInformation() {
    std::cout << "Owner is: " << m_owner->GetName() << std::endl;
}

所有者.h

#pragma once

class Book; // To avoid circular #includes

class Owner
{
public:
    Owner(std::string name, std::shared_ptr<Book> book);
    ~Owner();
    void OutputDetails();
    std::string GetName();
private:
    std::string m_name;
    std::shared_ptr<Book> m_book; // Owner hangs onto the book
};

所有者.cpp

#include "stdafx.h"
#include "Owner.h"
#include "Book.h"

Owner::Owner(std::string name, std::shared_ptr<Book> book) : m_name(name), m_book(book) {}

Owner::~Owner() {
    std::cout << "Owner Destroyed" << std::endl;
}

void Owner::OutputDetails() {
    std::cout << m_name << " owns " << std::endl;
    m_book->OutputDetails();
}

std::string Owner::GetName() {
    return m_name;
}

这是 main.cpp。在这种情况下, book 和 owner 之间存在强引用,一旦 _tmain 退出其作用域,就会发生内存泄漏。当我在各自的析构函数中插入断点时,不会调用 book 和 owner 的析构函数。

main.cpp

#include "stdafx.h"
#include <memory>
#include "Book.h"
#include "Owner.h"

int _tmain(int, _TCHAR*)
{
    {
        std::shared_ptr<Book> book = std::shared_ptr<Book>(new Book("Moby Dick"));
        std::shared_ptr<Owner> owner = std::shared_ptr<Owner>(new Owner("George Heriot", book));

        // Introduced a circular dependency so
        // neither gets deleted
        book->SetOwner(owner);

        owner->OutputDetails();
        book->OutputOwnerInformation();
    }

    return 0;
}

我想看看我是否可以 reset() 指针,以便调用析构函数并打破循环依赖。根据我对 shared_ptr.reset() 的理解,对象应该是空的。

http://www.cplusplus.com/reference/memory/shared_ptr/reset/

但是,我在两个析构函数中的断点都没有被击中。我的假设是,因为我已经重置了 book 和 owner,所以两者的引用计数都会下降到 0,并且当 _tmain 返回时它们会被销毁。

main2.cpp

#include "stdafx.h"
#include <memory>
#include "Book.h"
#include "Owner.h"

int _tmain(int, _TCHAR*)
{
    {
        std::shared_ptr<Book> book = std::shared_ptr<Book>(new Book("Moby Dick"));
        std::shared_ptr<Owner> owner = std::shared_ptr<Owner>(new Owner("George Heriot", book));

        // Introduced a circular dependency so
        // neither gets deleted
        book->SetOwner(owner);

        owner->OutputDetails();
        book->OutputOwnerInformation();

        owner.reset();
        book.reset();
    }

    return 0;
}

我知道这已经是很糟糕的代码,我可以使用 weak_ptr 来删除循环依赖,但我只是好奇为什么 reset() 不会破坏这种依赖。

【问题讨论】:

  • 但是你并没有打破循环,book.m_owner 仍然指向所有者,owner.m_book 仍然指向书籍。我想您可能会认为 main 中的共享指针和对象中的共享指针是相同的,但它们不是:它们只是 point 指向相同的对象并共享引用计数。
  • 如果你有循环所有权,根据定义你有一个设计错误。

标签: c++ c++11 shared-ptr reset cyclic-reference


【解决方案1】:

在重置之前尝试打印owner.use_count()book.use_count()。您将看到使用计数为 2。重置调用将使 ownerbook 将其计数减 1,但仍有其他 shared_ptr 对象与它们共享所有权并且您不重置,所以引用计数不会达到零。

如果您考虑一下,您应该意识到reset() 当然不能打破循环,因为reset() 的等价物无论如何都发生在shared_ptr 析构函数中。如果析构函数可以像那样破坏循环,那么首先创建循环就没有问题。

【讨论】:

  • 啊,我明白了。我想我明白现在发生了什么。在这种情况下,由于我已经重置了所有者和书籍,内存中是否存在书籍和所有者的狂野实例?
  • 不,它们不是“野生的”shared_ptr owner-&gt;m_bookbook 共享所有权,book-&gt;m_ownerowner 共享所有权。您只重置了owner 而不是m_book-&gt;owner。当您重置 owner 时,引用计数变为 1,并且您无法重置最后一个 shared_ptr,因为您刚刚丢弃了您拥有的唯一句柄。
  • 好的,感谢您清除该问题。我的意思是狂野,当我执行owner.reset()book.reset() 时,我在_tmain 中丢失了ownerbook 的唯一句柄,因此我无法检索到ownerbook 的句柄。
猜你喜欢
  • 2014-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-27
  • 2020-07-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多