C ++ 11, shared_ptr.reset () и циклические ссылки

У меня есть вопрос о поведении 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 выйдет из своей области. Деструкторы и для книги, и для владельца не вызываются, когда я вставляю точки останова в соответствующие деструкторы.

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/

Тем не менее, мои точки разрыва в обоих деструкторах не пострадали. Я предполагаю, что, поскольку я сбросил и книгу, и владельца, счетчик ссылок уменьшится до 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 () не нарушает эту зависимость.

2

Решение

Попробуйте распечатать owner.use_count() а также book.use_count() перед их сбросом. Вы увидите, что счетчики использования равны 2. Сброс вызовов сделает owner а также book уменьшить их количество на 1, но есть и другие shared_ptr объекты, которые владеют ими и не сбрасывают их, поэтому количество ссылок не достигает нуля.

Если вы думаете об этом, вы должны понимать, что, конечно, reset() не может прерывать циклы, потому что эквивалент reset() происходит в shared_ptr деструктор в любом случае. Если бы деструктор мог разорвать подобные циклы, то не было бы проблем с созданием циклов в первую очередь.

4

Другие решения