Understanding C++ std::shared_ptr

Viewed 2398

I have a question, please go through the following simple C++ program,

int main( )
{
 shared_ptr<int> sptr1( new int );
 shared_ptr<int> sptr2 = sptr1;
 shared_ptr<int> sptr3;
 shared_ptr<int> sptr4;
 sptr3 = sptr2;

 cout<<sptr1.use_count()<<endl;
 cout<<sptr2.use_count()<<endl;
 cout<<sptr3.use_count()<<endl;

 sptr4 = sptr2;

 cout<<sptr1.use_count()<<endl;
 cout<<sptr2.use_count()<<endl;
 cout<<sptr3.use_count()<<endl;

 return 0;
}

Output:

3
3
3
4
4
4

How does sptr1 and sptr3 objects know reference count is incremented as it prints 4.

As far as I know reference count is a variable in each shared_ptr object.

2 Answers

As far as i know reference count is a variable in each shared_ptr object.

No, the reference count is stored in a "control block" on the heap. Every shared_ptr instance points to the same "control block" and keeps it alive (until all instances and all weak_ptr instances that share ownership with them are dead).

A shared_ptr<T> is usually implemented as two pointers. One to the object data, and one to a structure that looks like this:

[strong reference count]
[weak reference count]
[type-erased destroyer fptr]
[type-erased destroyer data]

where the [object ptr] points to the actual object.

When you copy a shared_ptr, it creates another pointer to the above data (and to the actual object) and increments the [strong reference count] part of it. Weak pointers behave similarly, but instead increase the [weak reference count] (when strong is 0 and weak is non-zero- the type-erased destroyer is called but the control block remains).

As an aside, when you call make_shared it does:

[strong reference count]
[weak reference count]
[type-erased destroyer fptr]
[object data]

where the pointer-to-object now points to the to the [object data] at the end of the control block. This reduces the number of allocations to one.

A separate data and control block pointer is needed because of features like the fact that a shared_ptr to derived can be changed to a shared_ptr to base; that requires an adjustment to the pointer value in many cases. Similarly, the aliasing constructor of shared ptr allows the control block and the pointed to object to be completely unrelated.

Taken from @Estinox's answer-that-isn't-an-answer, here is a Channel9 talk on how shared ptr works.

Related