C++ 使用共享ptr时如何检测周期
shared_ptr是Boost库中的一个引用计数智能指针 引用计数的问题在于它不能处理循环。我想知道如何在C++中解决这个问题。p> 请不要提出诸如“不要循环”或“使用弱ptr”之类的建议 编辑 我不喜欢那些说只使用弱ptr的建议,因为很明显,如果你知道你将创建一个循环,那么你就不会有问题。如果在运行时生成共享的ptr,您也无法知道编译时会有一个周期C++ 使用共享ptr时如何检测周期,c++,garbage-collection,shared-ptr,reference-counting,C++,Garbage Collection,Shared Ptr,Reference Counting,shared_ptr是Boost库中的一个引用计数智能指针 引用计数的问题在于它不能处理循环。我想知道如何在C++中解决这个问题。p> 请不要提出诸如“不要循环”或“使用弱ptr”之类的建议 编辑 我不喜欢那些说只使用弱ptr的建议,因为很明显,如果你知道你将创建一个循环,那么你就不会有问题。如果在运行时生成共享的ptr,您也无法知道编译时会有一个周期 因此,请自行删除使用弱ptr的答案,因为我特别要求不要有此类答案…检测周期相当容易: 将计数设置为更大的数字,例如1000(具体大小取决于您的
因此,请自行删除使用弱ptr的答案,因为我特别要求不要有此类答案…检测周期相当容易:
- 将计数设置为更大的数字,例如1000(具体大小取决于您的应用程序)
- 从您感兴趣的pionter开始,并遵循它的指示
- 对于跟随的每个指针,减少计数
- 如果在到达指针链末端之前计数下降到零,则有一个循环
然而,它不是很有用。通常不可能解决ref计数指针的cycvle问题——这就是为什么发明了替代垃圾收集方案,如生成清理。我没有找到比绘制大型UML图和寻找循环更好的方法 为了进行调试,我使用一个实例计数器进入注册表,如下所示:
template <DWORD id>
class CDbgInstCount
{
public:
#ifdef _DEBUG
CDbgInstCount() { reghelper.Add(id, 1); }
CDbgInstCount(CDbgInstCount const &) { reghelper.Add(id, 1); }
~CDbgInstCount() { reghelper.Add(id, -1); }
#else
#endif
};
模板
类CDbgInstCount
{
公众:
#ifdef_调试
CDbgInstCount(){reghelper.Add(id,1);}
CDbgInstCount(CDbgInstCount常量&){reghelper.Add(id,1);}
~CDbgInstCount(){reghelper.Add(id,-1);}
#否则
#恩迪夫
};
我只是想把它添加到相关的类中,并查看一下注册表
(ID,如果以“XYZ!”等形式给出,将转换为字符串。不幸的是,您不能指定字符串常量作为模板参数)可能是
boost::weak_ptr
和boost::shared_ptr
的组合?文章可能会引起兴趣。共享\u ptr
表示所有权关系。而弱\u ptr
代表意识。多个对象相互拥有意味着您在体系结构方面存在问题,这可以通过将一个或多个own对象更改为aware-of对象(即,weak_-ptr
)来解决
我不明白为什么建议
弱ptr
被认为是无用的。我知道你说过“不弱ptr”,但为什么不呢?让你的头和尾巴之间的距离很小,而尾巴和头部之间的距离也很小,这会阻止这种循环。请看这篇文章的图表 找到循环的一般解决方案可以在这里找到:
这假设您知道列表中对象的结构,并且可以遵循每个对象中包含的所有指针。我理解您被轻率地告知使用
弱\u ptr
来破坏循环引用时的烦恼,而我自己当被告知循环引用是糟糕的编程风格时,几乎感到愤怒
你会特别询问如何识别循环引用。事实是,在一个复杂的项目中,一些参考周期是间接的,很难发现
答案是,您不应该做出错误的声明,从而使您容易受到循环引用的攻击。我是认真的,我在批评一种非常流行的做法——盲目地将共享ptr用于所有事情
在设计中,您应该清楚哪些指针是所有者,哪些是观察者
对于所有者,请使用共享\u ptr
对于观察者,请使用弱\u ptr
-所有这些,而不仅仅是您认为可能属于某个周期的部分
如果您遵循这种做法,那么循环引用将不会导致任何问题,您不必担心它们。
当然,当你想使用它们时,你需要编写大量代码来将所有这些
弱ptr
s转换为共享ptr
s,Boost真的不能胜任这项工作。回答老问题,你可以尝试使用侵入式指针,它可以帮助你计算资源被引用的次数
#include <cstdlib>
#include <iostream>
#include <boost/intrusive_ptr.hpp>
class some_resource
{
size_t m_counter;
public:
some_resource(void) :
m_counter(0)
{
std::cout << "Resource created" << std::endl;
}
~some_resource(void)
{
std::cout << "Resource destroyed" << std::endl;
}
size_t refcnt(void)
{
return m_counter;
}
void ref(void)
{
m_counter++;
}
void unref(void)
{
m_counter--;
}
};
void
intrusive_ptr_add_ref(some_resource* r)
{
r->ref();
std::cout << "Resource referenced: " << r->refcnt()
<< std::endl;
}
void
intrusive_ptr_release(some_resource* r)
{
r->unref();
std::cout << "Resource unreferenced: " << r->refcnt()
<< std::endl;
if (r->refcnt() == 0)
delete r;
}
int main(void)
{
boost::intrusive_ptr<some_resource> r(new some_resource);
boost::intrusive_ptr<some_resource> r2(r);
std::cout << "Program exiting" << std::endl;
return EXIT_SUCCESS;
}
您可能需要一种垃圾收集器技术,例如。该算法的思想是:
shared_ptr
,因此您无法到达的任何仍然存在的指针都应被视为周期的成员
实施
下面我将介绍一个非常简单的示例,说明如何实现算法的sweep()
部分,但它将reset()
收集器上的所有剩余指针
此代码存储共享\u ptr
指针。类收集器
负责跟踪所有指针,并在执行sweep()
时删除它们
#include <vector>
#include <memory>
class Cycle_t;
typedef std::shared_ptr<Cycle_t> Ref_t;
// struct Cycle;
struct Cycle_t {
Ref_t cycle;
Cycle_t() {}
Cycle_t(Ref_t cycle) : cycle(cycle) {}
};
struct collector {
// Note this vector will grow endlessy.
// You should find a way to reuse old links
std::vector<std::weak_ptr<Cycle_t>> memory;
// Allocate a shared pointer keeping
// a weak ref on the memory vector:
inline Ref_t add(Ref_t ref) {
memory.emplace_back(ref);
return ref;
}
inline Ref_t add(Cycle_t value) {
Ref_t ref = std::make_shared<Cycle_t>(value);
return add(ref);
}
inline Ref_t add() {
Ref_t ref = std::make_shared<Cycle_t>();
return add(ref);
}
void sweep() {
// Run a sweep algorithm:
for (auto& ref : memory) {
// If the original shared_ptr still exists:
if (auto ptr = ref.lock()) {
// Reset each pointer contained within it:
ptr->cycle.reset();
// Doing this will trigger a deallocation cascade, since
// the pointer it used to reference will now lose its
// last reference and be deleted by the reference counting
// system.
//
// The `ptr` pointer will not be deletd on the cascade
// because we still have at least the current reference
// to it.
}
// When we leave the loop `ptr` loses its last reference
// and should be deleted.
}
}
};
我用Valgrind对它进行了测试,没有列出内存泄漏或“仍然可以访问”的块,所以它可能按预期工作
关于这一实施的一些注意事项:
共享\u ptr
(其工作原理类似于引用计数GC)来实现这样一个垃圾收集器,因为标记和扫描算法已经处理了这个任务#include <vector>
#include <memory>
class Cycle_t;
typedef std::shared_ptr<Cycle_t> Ref_t;
// struct Cycle;
struct Cycle_t {
Ref_t cycle;
Cycle_t() {}
Cycle_t(Ref_t cycle) : cycle(cycle) {}
};
struct collector {
// Note this vector will grow endlessy.
// You should find a way to reuse old links
std::vector<std::weak_ptr<Cycle_t>> memory;
// Allocate a shared pointer keeping
// a weak ref on the memory vector:
inline Ref_t add(Ref_t ref) {
memory.emplace_back(ref);
return ref;
}
inline Ref_t add(Cycle_t value) {
Ref_t ref = std::make_shared<Cycle_t>(value);
return add(ref);
}
inline Ref_t add() {
Ref_t ref = std::make_shared<Cycle_t>();
return add(ref);
}
void sweep() {
// Run a sweep algorithm:
for (auto& ref : memory) {
// If the original shared_ptr still exists:
if (auto ptr = ref.lock()) {
// Reset each pointer contained within it:
ptr->cycle.reset();
// Doing this will trigger a deallocation cascade, since
// the pointer it used to reference will now lose its
// last reference and be deleted by the reference counting
// system.
//
// The `ptr` pointer will not be deletd on the cascade
// because we still have at least the current reference
// to it.
}
// When we leave the loop `ptr` loses its last reference
// and should be deleted.
}
}
};
Collector collector;
int main() {
// Build your shared pointers:
{
// Allocate them using the collector:
Ref_t c1 = collector.add();
Ref_t c2 = collector.add(c1);
// Then create the cycle:
c1.get()->cycle = c2;
// A normal block with no cycles:
Ref_t c3 = collector.add();
}
// In another scope:
{
// Note: if you run sweep an you still have an existing
// reference to one of the pointers in the collector
// you will lose it since it will be reset().
collector.sweep();
}
}
void mark(Ref_t root) {
root->marked = true;
// For each other Ref_t stored on root:
for (Ref_t& item : root) {
mark(item);
}
}
void sweep() {
// Run a sweep algorithm:
for (auto& ref : memory) {
// If it still exists:
if (auto ptr = ref.lock()) {
// And is marked:
if (ptr->marked) {
ptr->marked = false;
} else {
ptr->cycle.reset();
}
}
}
}