2

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

所有者.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() がこの依存関係を壊さない理由に興味があります。

4

1 に答える 1

4

それらをリセットする前に印刷owner.use_count()してみてください。book.use_count()使用カウントが 2 であることがわかります。リセット コールはカウントownerbook1 減らしますが、それらと所有権を共有し、リセットしない他のshared_ptrオブジェクトがまだあるため、参照カウントはゼロに達しません。 .

考えてみれば、もちろんreset()、サイクルを壊すことはできないことに気付くはずです。とにかく、デストラクタreset()で同等のことが起こるからです。shared_ptrデストラクタがそのようなサイクルを壊すことができれば、そもそもサイクルを作成することに問題はありません。

于 2014-12-18T15:52:18.617 に答える