C++ 引用计数智能指针的引用计数是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/725142/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does a reference-counting smart pointer's reference counting work?
提问by Srikanth
In other words, how does the implementation keeps track of the count?
换句话说,实现如何跟踪计数?
Is there a map-like object maintained which is accessible by all the shared_ptr
instances whose key is the pointer's address and value is the number of references? If I've to implement a shared_ptr
, this is the first idea that's coming to my mind.
是否维护了一个类似映射的对象,它可以被所有shared_ptr
实例访问,其键是指针的地址,值是引用的数量?如果我必须实现一个shared_ptr
,这是我想到的第一个想法。
Is there a possibility for a memory leak in case of these reference-counting smart pointers? If so, how can I avoid them?
在这些引用计数智能指针的情况下,是否有可能发生内存泄漏?如果是这样,我该如何避免它们?
回答by Ferruccio
I've seen two different non-intrusive approaches to this:
我已经看到了两种不同的非侵入式方法:
- The smart pointer allocates a small block of memory to contain the reference counter. Each copy of the smart pointer then receives a pointer to the actual object and a pointer to the reference count.
- In addition to an object pointer, each smart pointer contains a previous and next pointer, thereby forming a doubly-linked list of smart pointers to a particular object. The reference count is implicit in the list. When a smart pointer is copied, it adds itself to the list. Upon destruction, each smart pointer removes itself from the list. If it's the last one in the list it then frees the referenced object as well.
- 智能指针分配一小块内存来包含引用计数器。智能指针的每个副本然后接收一个指向实际对象的指针和一个指向引用计数的指针。
- 除了对象指针之外,每个智能指针还包含一个上一个和下一个指针,从而形成一个指向特定对象的智能指针的双向链表。引用计数隐含在列表中。当智能指针被复制时,它会将自己添加到列表中。销毁时,每个智能指针都会从列表中删除自己。如果它是列表中的最后一个,它也会释放引用的对象。
If you go hereand scroll to the bottom, there is an excellent diagram which explains these methods much more clearly.
如果你去这里滚动到底部,有一个很好的图表,可以更清楚地解释这些方法。
回答by sharptooth
Creating a memory leak with reference-counting smart pointers is very easy. Just create any graph-like structure of objects that has a cycle in the graph. The objects in the cycle will prevent each other from being released. This can't be resolved automatically - for example, when you create a double-link list you have to take care of never removing more than one object at a time.
使用引用计数智能指针创建内存泄漏非常容易。只需创建在图中具有循环的对象的任何类似图的结构即可。循环中的对象会相互阻止被释放。这无法自动解决 - 例如,当您创建一个双链接列表时,您必须注意不要一次删除多个对象。
回答by sharptooth
Each smart pointer object contains a shared reference count - one for every raw pointer.
每个智能指针对象都包含一个共享引用计数——每个原始指针一个。
You could take a look at thisarticle. This implementation stores these in a separate object which is copied around. You could also take a look at boost's documentationor take a look at the Wikipedia articleon smart pointers.
你可以看看这篇文章。此实现将这些存储在一个单独的对象中,该对象被复制。您还可以查看boost 的文档或查看有关智能指针的维基百科文章。
回答by C?t?lin Piti?
As far as I remember, there was the problem of reference counting pointer treated in a chapter of Effective C++.
据我所知,在Effective C++的一章中处理过引用计数指针的问题。
In principle, you have the "light" pointer class, containing a pointer to a class holding the reference which knows to increment/decrement reference and destroy the pointer object. That reference counting class points to the object to be referenced.
原则上,您拥有“轻量级”指针类,其中包含一个指向类的指针,该类持有该引用,该类知道递增/递减引用并销毁指针对象。该引用计数类指向要引用的对象。
回答by bayda
No. shared_ptr just keep one additional pointer for reference counting.
不,shared_ptr 只保留一个额外的指针用于引用计数。
When you make copy of shared_ptr object it copy pointer with count of references, increase it, and copy pointer on contained object.
当您复制 shared_ptr 对象时,它会复制带有引用计数的指针,增加它,并在包含的对象上复制指针。
回答by David Rodríguez - dribeas
Many answers address the way the reference count is stored (it is stored in a shared memory for all shared_ptr that hold the same native pointer), but most elude the problem of leaks.
许多答案解决了引用计数的存储方式(它存储在共享内存中,用于保存相同本机指针的所有 shared_ptr),但大多数都避免了泄漏问题。
The easiest way of leaking memory with reference counted pointers is creating cycles. As an example, a doubly linked list where all the pointers are shared_ptr with at least two elements is guaranteed not to be deleted. Even if external pointers are freed, the internal pointers will still count, and the reference count will not reach 0. That is, at least, with the most na?ve implementation.
使用引用计数指针泄漏内存的最简单方法是创建循环。作为一个例子,一个双向链表,其中所有的指针都是至少有两个元素的 shared_ptr 保证不会被删除。即使释放了外部指针,内部指针仍然会计数,并且引用计数不会达到 0。也就是说,至少在最幼稚的实现中是这样。
The easiest solution to the cycle problem is mixing shared_ptr (reference counted pointers) with weak pointers that do not share the ownership of the object.
循环问题的最简单解决方案是将 shared_ptr(引用计数指针)与不共享对象所有权的弱指针混合。
Shared pointers will share both the resource (pointer) and the additional reference_count information. When you use weak pointers, the reference count is doubled: there is a shared pointer reference count and a weak pointer reference count. The resource is released whenever the shared pointer count reaches 0, but the reference_count information is left alive until the last weak pointer is released.
共享指针将共享资源(指针)和附加的 reference_count 信息。当您使用弱指针时,引用计数加倍:有一个共享指针引用计数和一个弱指针引用计数。每当共享指针计数达到 0 时,就会释放资源,但 reference_count 信息保持活动状态,直到最后一个弱指针被释放。
In the doubly linked list, the external reference is held in a shared_ptr, while the internal links are just weak_ptr. Whenever there are no external references (shared_ptr) the elements of the list are released, deleting the weak references. At the end all weak references have been deleted and the last weak pointer to each resources frees the reference_count information.
在双向链表中,外部引用保存在 shared_ptr 中,而内部链接只是weak_ptr。每当没有外部引用(shared_ptr)时,列表的元素就会被释放,删除弱引用。最后,所有弱引用都被删除,指向每个资源的最后一个弱指针释放了 reference_count 信息。
It is less confusing than the above text seems... I'll try again later.
它不像上面的文字看起来那么混乱......我稍后再试。
回答by Abhishek A Udupa
The class that implements RC basically keeps count of number of references (from other objects of the class, including its own) to the memory address that it is managing. The memory is freed only when the reference count to the memory address is zero.
实现 RC 的类基本上保持对它正在管理的内存地址的引用数(来自类的其他对象,包括它自己的)。仅当对内存地址的引用计数为零时才释放内存。
Let's look at some code:
让我们看一些代码:
template <class T>
class SharedPtr
{
T* m_ptr;
unsigned int* r_count;
public:
//Default Constructor
SharedPtr(T* ptr) :m_ptr{ ptr }, r_count{ ptr ? new unsigned int : nullptr }
{
if (r_count)
{
*r_count = 1;
}
}
//Copy Constructor
SharedPtr(SharedPtr& ptr) :m_ptr{ ptr.m_ptr }, r_count{ ptr.m_ptr ? new unsigned int : nullptr }
{
if (ptr.r_count)
{
++(*ptr.r_count);
r_count = ptr.r_count;
m_ptr = ptr.m_ptr;
}
}
//Copy Assignment
SharedPtr& operator=(SharedPtr& ptr)
{
if (&ptr == this)
return *this;
if (ptr.r_count)
{
delete m_ptr;
++(*ptr.r_count);
r_count = ptr.r_count;
m_ptr = ptr.m_ptr;
}
return *this;
}
//Destructor
~SharedPtr()
{
if (r_count)
{
--(*r_count);
if (!(*r_count))
{
delete m_ptr;
delete r_count;
}
}
}
};
Here's the detail of how the SharedPtr
class above works:
以下是上述SharedPtr
类如何工作的详细信息:
Internal Variables
内部变量
The internal pointerm_ptr
内部指针m_ptr
A pointer of the SharedPtr
class, which is the actual pointer used to manage the memory in question. This pointer variable is shared across multiple SharedPtr
objects, which is why we need a reference counting system to keep track of how many SharedPtr
objects are managing the memory pointed to by this pointer at any point of time during a program's lifetime.
SharedPtr
类的指针,它是用于管理相关内存的实际指针。这个指针变量在多个SharedPtr
对象之间共享,这就是为什么我们需要一个引用计数系统来跟踪有多少SharedPtr
对象在程序生命周期的任何时间点管理着这个指针指向的内存。
The reference counterr_count
参考计数器r_count
This is a pointer to an integer type variable, which is also shared across multiple SharedPtr
objects managing the same memory. This is shared because, every SharedPtr
object managing the memory should be aware of the count of every other SharedPtr
object that is managing the same memory. The way to achieve this is by having a common reference counter referred to by SharedPtr
objects of the same family.
这是一个指向整数类型变量的指针,它也被SharedPtr
管理相同内存的多个对象共享。这是共享的,因为SharedPtr
管理内存的每个对象都应该知道SharedPtr
管理相同内存的每个其他对象的计数。实现这一点的方法是SharedPtr
让同一族的对象引用一个公共引用计数器。
Every time a new SharedPtr
object is materialized to manage a memory already being managed by other SharedPtr
object/s, the r_count goes up by 1. It is also decremented by 1 when a SharedPtr
object dies, so that other SharedPtr
objects ‘know' that one of their family members who was managing the memory maintained by the family has died and no-longer managing the memory.
每次实现一个新SharedPtr
对象来管理已经由其他SharedPtr
对象管理的内存时,r_count 增加 1。当一个SharedPtr
对象死亡时,它也会减 1 ,以便其他SharedPtr
对象“知道”它们的家族中的一个管理由家庭维护的记忆的成员已经死亡,不再管理记忆。
Default Constructor
默认构造函数
When a new SharedPtr
object is created and initialized by a heap allocated memory, this constructor is called where the internal pointer m_ptr
is initialized to the heap allocated memory address that needs managing. Since this is the first and the only reference to that pointer, the reference counter r_count
is set to 1. Nothing interesting happens here.
当一个新SharedPtr
对象被堆分配的内存创建和初始化时,这个构造函数被调用,其中内部指针m_ptr
被初始化为需要管理的堆分配的内存地址。由于这是对该指针的第一个也是唯一的引用,因此引用计数器r_count
被设置为 1。这里没有任何有趣的事情发生。
Copy Constructor and Copy Assignment
复制构造函数和复制赋值
This is where the ‘real' reference counting happens.
这就是“真正的”引用计数发生的地方。
Whenever a new SharedPtr
object is made using another SharedPtr
object or an existing SharedPtr
is made to reference another SharedPtr
i.e basically when a new SharedPtr
object (either existing or newly created) is made to manage a memory that was already being managed by other SharedPtr
object/s, the the internal pointer variable m_ptr
of this new manager is made to point at the memory address to be managed and the reference count of the family goes up by 1.
每当SharedPtr
使用另一个SharedPtr
对象创建新对象或使现有对象SharedPtr
引用另一个对象SharedPtr
时,即基本上当使新SharedPtr
对象(现有对象或新创建的)管理已由其他SharedPtr
对象管理的内存时,内部m_ptr
使这个新管理器的指针变量指向要管理的内存地址,并且该系列的引用计数增加 1。
Destructor
析构函数
Smart Pointers are designed to free the memory they're managing when they die. In the case of SharedPtr
, it ensures that there are no other references to the memory being managed before freeing the memory. All of these happen in the object's Destructor.
智能指针旨在释放它们死后管理的内存。在 的情况下SharedPtr
,它确保在释放内存之前没有其他对正在管理的内存的引用。所有这些都发生在对象的析构函数中。
As you can see in the code, the object frees the memory only if the reference count to the memory is 0, before it dies.
正如您在代码中看到的那样,只有在内存的引用计数为 0 时,对象才会在它消亡之前释放内存。
This is important because, you see, if a SharedPtr
object frees the memory when r_count isn't 0, other SharedPtr
objects managing the same memory would try to access it sometime after and the result would be a program crash.
这很重要,因为您会看到,如果一个SharedPtr
对象在 r_count 不为 0 时释放内存,则SharedPtr
管理相同内存的其他对象将在某个时间之后尝试访问它,结果将是程序崩溃。
The SharedPtr
ensures this does not happen by giving the responsibility of freeing memory to the last surviving object that is managing a memory. Due to the design of the SharedPtr
, all of this happens automatically without the programmer's intervention.
在SharedPtr
确保这不会给予释放内存到被管理存储器最后生存对象的责任发生。由于 的设计SharedPtr
,所有这些都自动发生,无需程序员干预。
This is how reference counting works.
这就是引用计数的工作原理。
Reference counting is like the routine of couple of roommates: who leaves the room last has the responsibility of locking the main door. For that to happen seamlessly, every roommate should be aware if he's the last one to leave the room.
引用计数就像几个室友的例行公事:谁最后离开房间有责任锁上大门。为了让这一切顺利进行,每个室友都应该知道他是否是最后一个离开房间的人。