C ++ 11,shared_ptr.reset()和循环引用

时间:2014-12-18 15:45:04

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

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

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

Book.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;
}

Owner.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
};

Owner.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。在这种情况下,书籍和所有者对彼此具有强烈的引用,并且一旦_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;
}

我想看看是否可以重置()指针,以便调用析构函数并打破循环依赖。根据我对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()不会破坏这种依赖。

1 个答案:

答案 0 :(得分:4)

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

如果你仔细想想,你应该意识到reset()当然不能打破周期,因为reset()的等价物无论如何都会在shared_ptr析构函数中发生。如果析构函数可以打破这样的循环,那么首先创建循环就没有问题了。