C++ 打印 shared_ptr 在树中使用计数

C++ Printing shared_ptr use count in tree

我无法理解为什么当我以不同方式打印树时,我的共享指针得到不同数量的 use_counts()。

使用下面的代码,当我调用方法 "one->Print()" 时,我似乎错误地为一个的直接子级获得了 2 个引用,但是当使用 "one->Print(one)" 时,我得到了正确数量的引用, 即 1.

我如何更改我的代码,以便 "one->Print()" returns 树中所有节点的正确引用数?

#include <iostream>
#include <memory>

template<class T> using sp = std::shared_ptr<T>;

struct Node {

    int value; 
    sp<Node> child;
    Node(int value): value {value} {}

    inline void Print() const {
        Print(std::make_shared<Node>(*this));
    }

    inline void Print(const sp<Node>& ptr) const {
        Print(ptr, "", false);
    }

    void Print(const sp<Node>& ptr, const std::string& prepend, bool isEnd) const {
        if(ptr != nullptr) {
            std::cout << prepend << (isEnd ? "└────" : "├────"); 
            std::cout << " " << ptr->value << " (" << ptr.use_count() << ")" << std::endl;
        } else {
            std::cout << " " << ptr->value << std::endl;
        }
        if(ptr->child != nullptr) {
            Print(ptr->child, prepend + (isEnd ? "     " : "│     "), false); 
        }
    }

};

int main(int argc, char * argv[])
{
    sp<Node> one = std::make_shared<Node>(1);
    one->child = std::make_shared<Node>(2); 
    one->child->child = std::make_shared<Node>(3);
    one->child->child->child = std::make_shared<Node>(4);
    one->child->child->child = std::make_shared<Node>(5);

    one->Print(); 

    one->Print(one);

    return 0;
}

输出如下:

一个->打印();

├──── 1 (1)
│     └──── 2 (2)
│          └──── 3 (1)
│               └──── 5 (1)

一个->打印(一个);

├──── 1 (1)
│     └──── 2 (1)
│          └──── 3 (1)
│               └──── 5 (1)

当您调用 Print(ptr) 时,它作为对共享指针的引用传递并且不进行复制。

当您调用 Print() 时,它会复制共享指针并将其传递给 Print(ptr)

正是那个副本增加了引用计数。如果您不想发生这种情况,请不要复制。应该可以直接传this作为参考。

这是因为您使用 std::make_shared<Node>(*this) 创建了 *this (one) 的副本,后者将复制 this->child (one->chile) 并增加参考计数器。


你想要的可能是继承自enable_shared_from_this并使用shared_from_this,那么你会得到

├──── 1 (2)
│     └──── 2 (1)
│          └──── 3 (1)
│               └──── 5 (1)

此外,如果您不需要引用计数器(通常为真),如果您不打算管理资源,则不需要智能指针,您可以简单地接受一个 Node* 。实际上,您可以使用 this 并删除此示例中的所有指针。


示例代码。 (使用 this 而不是 ptr

struct Node: std::enable_shared_from_this<Node> {

    int value; 
    sp<Node> child;
    Node(int value): value {value} {}

    void Print() const {
        Print("", false);
    }

    void Print(const std::string& prepend, bool isEnd) const {
        std::cout << prepend << (isEnd ? "└────" : "├────"); 
        std::cout << " " << this->value << " (" << shared_from_this().use_count()-1 /*remove current count*/ << ")" << std::endl;
        if(this->child) {
            this->child->Print(prepend + (isEnd ? "     " : "│     "), false); 
        }
    }

};

你也可以使用week_from_this (c++17), wandbox example

如其他答案中所述,通过调用 std::make_shared<Node>(*this) 您实际上是在调用默认复制构造函数。因此命中某处 newObj->child = (*this).child(i,e - std::shared_ptr 的重载 =(赋值运算符)),从而将引用计数增加到 2。当函数 Print() 退出时,shared_ptr 被销毁,因此将引用计数减少到 1。

当您使用引用调用时,不会创建任何内容,因此您会看到预期的结果。

解决此问题的一种方法是重载您的函数以接受 const Node & 然后您可以使用 *this.