Make_shared, almost a silver bullet

22 04 2012

Herb Sutter just published a new Guru of the Week in which he claims one should always use make_shared when created shared pointers.

This is very good advice (as one can expect from Herb) but I can’t resist the urge to nitpick.

As Herb states the advantage of using make_shared is that when you use plain new the shared_ptr object must allocate the reference counting block separately from the (existing) tracked object

Memory layout when using new

On the other hand when using make_shared both the tracked object and the reference counting can be allocated together.

Memory layout when using make_shared

This has the following advantages:

  • One less memory allocation (and deallocation), this is both faster and takes up less memory (the allocator overhead for size tracking)
  • Increased data locality, the ref counts and the object are probably in the same memory page (perhaps even the same cache line)

However there could be rare cases in which the make_shared implementation has a drawback. Consider what happens if we use weak_ptrs to break cycles in our ownership graph and the object’s strong references expire while some weak references hold on. This means that the pointed at object must be destroyed but we still need the reference counts (specifically the weak reference count).

So in the case where we used plain-old new we get:

void releaseStrongRef()
{
    if (--data_->strongRefs_ == 0)
        delete data_->value_; // destroy object and free its memory
    if (data_->weakRefs_ == 0)
        delete data_;
}

void releaseWeakRef()
{
    if (--data_->weakRefs_ == 0)
        delete data_;
}

While the make_shared implementation will have to hold on to the memory till the weak references are released:

void releaseStrongRef()
{
    if(--data_->strongRefs_ == 0) 
        data_->value_->~T();// explicitly destroy object, memory isn't freed
    if(data_->weakRefs_ == 0)
        delete data_;
}

void releaseWeakRef()
{
    if(--data_->weakRefs_ == 0)
        delete data_;
}

So if you’re expecting to use weak_ptrs that will outlive your pointed to object and you need your memory back ASAP consider not using make_shared.


Actions

Information

9 responses

23 04 2012
Waldemar Pawlaszek

Yes. It’s an old problem that e.g. emerged few time on stackoverflow and I’ve written about it in the question ( http://herbsutter.com/2012/01/20/gotw-103-smart-pointers-part-1-difficulty-310/#comment-4714 ). It isn’t obvious (it didn’t cross my mind when I’ve read about make_shared) and it should be pointed out explicitly when advocating consistent usage of make_shared. Fortunately Mr. Sutter did mention it.

Additionally there are many pitfalls in shared_ptr’s that might be painful to discover when you stumble upon it. For example enable_shared_from_this does not work well with multiple inheritance: try to create few classes each inherited from enable_shared_from_this and declare one derived class inherited from all of them, create an instance, cast it few times and see how each shared_from_this behaves.

23 04 2012
Motti Lanzkron

Do’h just when I thought I had an original thought 🙂

BTW where does Mr. Sutter mention the weak_ptr issue? I didn’t see it.

23 04 2012
Waldemar Pawlaszek

It’s not easy to find anything new in C++ world 🙂

Not directly, but it is mentioned in two bullets at the beginning of point 2 while defining “strong reference” (that the memory might be deallocated at the destruction of last of it) and “weak reference” (that it is deallocated at the destruction of last of it if it was not already). Although in my opinion little too-not-directly and it should have been noted explicitly.

25 04 2012
Puckering Pink Hexagon

Just FYI if you’re coming from Java, use of leading underscores (‘_blah’) anywhere is discouraged in C++, because it reduces portability. Besides global actual runtime/linktime symbols which could clash with the OS, I believe there’s danger from macros, meaning you’re not safe even using them as member vars (I could have sworn Herb Sutter wrote something about this in one or more of his books but I can’t find a reference to it). Here’s this, instead – http://bytes.com/topic/c/answers/171648-leading-underscore. Note that this goes further than the standard’s reserving /double/ leading underscores, and leading underscores followed by a capital letter. Regardless of whether you’ll ever use your code on obscure, badly-built OSes, it’s a bit annoying to the rest of us.

25 04 2012
Motti Lanzkron

You’re right, I’ve been coding too much C# recently :(, actually AFAIK leading underlines are only reserved for global or namespace level entities and should be OK in class members but I’ll change my sample code to trailing underscores anyway.

12 05 2018
How a weak_ptr Might Prevent Full Memory Cleanup of Managed Object – DEVELOPPARADISE

[…] Make_shared, almost a silver bullet | I will not buy this blog, it is scratched!. […]

24 09 2018
Why Make_shared-Note.lhm.im

[…] make_shared只分配一次内存, 这看起来很好. 减少了内存分配的开销. 问题来了,weak_ptr会保持控制块(强引用, 以及弱引用的信息)的生命周期, 而因此连带着保持了对象分配的内存, 只有最后一个weak_ptr离开作用域时, 内存才会被释放. 原本强引用减为0时就可以释放的内存, 现在变为了强引用, 若引用都减为0时才能释放, 意外的延迟了内存释放的时间. 这对于内存要求高的场景来说, 是一个需要注意的问题. 关于这个问题可以看这里make_shared, almost a silver bullet […]

13 10 2020
智能指针那些琐事儿 - 🏠Thiefuniverse

[…] 2.Make_shared, almost a silver bullet […]

25 06 2022
c 释放内存的方法(c 获得内存使用情况)-再会吧

[…] Make_shared, almost a silver bullet […]

Leave a reply to Motti Lanzkron Cancel reply