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
.
我无法理解为什么当我以不同方式打印树时,我的共享指针得到不同数量的 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
.