As illustrated in the code here, the size of the object returned from make_shared is two pointers.
However, why doesn’t make_shared work like the following (assume T is the type we’re making a shared pointer to):
The result of
make_sharedis one pointer in size, which points to of allocated memory of sizesizeof(int) + sizeof(T), where the int is a reference count, and this gets incremented and decremented on construction/destruction of the pointers.
unique_ptrs are only the size of one pointer, so I’m not sure why shared pointer needs two. As far as I can tell, all it needs a reference count, which with make_shared, can be placed with the object itself.
Also, is there any implementation that is implemented the way I suggest (without having to muck around with intrusive_ptrs for particular objects)? If not, what is the reason why the implementation I suggest is avoided?
In all implementations I’m aware of,
shared_ptrstores the owned pointer and the reference count in the same memory block. This is contrary to what other answers are saying. Additionally a copy of the pointer will be stored in theshared_ptrobject. N1431 describes the typical memory layout.It is true that one can build a reference counted pointer with sizeof only one pointer. But
std::shared_ptrcontains features that absolutely demand a sizeof two pointers. One of those features is this constructor:One pointer in the
shared_ptris going to point to the control block owned byr. This control block is going to contain the owned pointer, which does not have to bep, and typically isn’tp. The other pointer in theshared_ptr, the one returned byget(), is going to bep.This is referred to as aliasing support and was introduced in N2351. You may note that
shared_ptrhad a sizeof two pointers prior to the introduction of this feature. Prior to the introduction of this feature, one could possibly have implementedshared_ptrwith a sizeof one pointer, but no one did because it was impractical. After N2351, it became impossible.One of the reasons it was impractical prior to N2351 was because of support for:
Here,
p.get()returns aB*, and has generally forgotten all about the typeA. The only requirement is thatA*be convertible toB*.Bmay derive fromAusing multiple inheritance. And this implies that the value of the pointer itself may change when converting fromAtoBand vice-versa. In this example,shared_ptr<B>needs to remember two things:B*whenget()is called.A*when it is time to do so.A very nice implementation technique to accomplish this is to store the
B*in theshared_ptrobject, and theA*within the control block with the reference count.