gold 513062 640

C++ Core Guidelines: Rules for Smart Pointers

There were a lot of C++ experts who said that smart pointers were the essential feature of C++11. Today, I will write about smart pointers in C++.

The C++ core guidelines have thirteen rules for smart pointers. Half of them deal with their owner semantics; half of them with the question: How should you pass a shared pointer to a function?

gold 513062 640

Here is an overview of the rules.

The first five rules (R.20 – R.24) are quite obvious. I have written a few posts about them. Let me paraphrase the rules and refer to my previous posts.

A std::unique_ptr is an exclusive owner of its resource; therefore, you can not copy it but only move it. In contrast, a std::shared_pointer shares ownership. If you copy or copy assign a shared pointer, the reference counter will automatically be increased; if you delete or reset a shared pointer, the reference counter will be decreased. If the reference counter becomes zero, the underlying resource will be deleted. Because of this management overhead, you should use a std::unique_ptr, if possible (R.21).

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    This overhead becomes, in particular true if you create a std::shared_ptr. Creating a std::shared_ptr requires the allocation of the resource and the reference counter, which is, in sum, quite an expensive job; therefore, you should use the factory function std::make_shared (R.22). std::make_shared makes only one allocation. This is a big performance improvement for std::shared_ptr. In comparison, in the post “Memory and Performance Overhead of Shared Pointers” the differences between creating and deleting raw pointers and shared pointers including the factory functions std::make_shared and std::make_unique.

    There is an additional, vital reason to create an std::shared_ptr with std::make_shared, and an std::unique_ptr with std::make_unique: no memory leak (R.22 and R.23). Using two invocations of std::shared_ptr or std::unique_ptr in one expression can cause a memory leak if an exception happens. I was hoping you could read the details about this issue in my last post: C++ Core Guidelines: Rules for Allocating and Deallocating (R.13).

    A std::weak_ptr is not a smart pointer. A std::weak_ptr is no owner and lends only the resource from its std::shared_ptr. Its interface is quite limited. Using the method lock on a std::weak_ptr, you can lift a std::weak_ptr to a std::shared_ptr. Of course, you have a question: Why do we need a std::weak_ptr? A std::weak_ptr helps to break the cycles of std::shared_ptr (R.24). These cycles are the reason a std::shared_ptr will not automatically release its resource. Or to say it the other way around. If you have a cycle of shared pointers, you will have a memory leak. Read the details of std::weak_ptr and how you can use them to overcome memory leaks with std::shared_ptr in my previous post std::weak_ptr.

    Now I’m done with my summary of smart pointers. That is more or less general knowledge of smart pointers. This will not hold for the remaining rules. They deal with the question: How should you pass a shared pointer to a function?

    R.30: Take smart pointers as parameters only to explicitly express lifetime semantics

    This rule is a little bit tricky. If you pass a smart pointer as a parameter to a function and you use this function only as the underlying resource of the smart pointer, you do something wrong. In this case, you should use a pointer or a reference as a function parameter because you don’t have the lifetime semantics of a smart pointer.

    Let me give you an example of a smart pointer’s quite sophisticated lifetime management.

     

    // lifetimeSemantic.cpp
    
    #include <iostream>
    #include <memory>
    
    void asSmartPointerGood(std::shared_ptr<int>& shr){
      std::cout << "shr.use_count(): " << shr.use_count() << std::endl;  // (3)
      shr.reset(new int(2011));                                          // (5)
      std::cout << "shr.use_count(): " << shr.use_count() << std::endl;  // (4)
    }
    
    void asSmartPointerBad(std::shared_ptr<int>& shr){
      // doSomethingWith(*shr);
      *shr += 19;
    }
    
    int main(){
      
      std::cout << std::endl;
      
      auto firSha = std::make_shared<int>(1998);
      auto secSha = firSha;
      std::cout << "firSha.use_count(): " << firSha.use_count() << std::endl;  // (1)
      
      std::cout << std::endl;
      
      asSmartPointerGood(firSha);                                              // (2)
      
      std::cout << std::endl;
      
      std::cout << "*firSha: " << *firSha << std::endl;
      std::cout << "firSha.use_count(): " << firSha.use_count() << std::endl;
      
      std::cout << std::endl;
      
      std::cout << "*secSha: " << *secSha << std::endl;
      std::cout << "secSha.use_count(): " << secSha.use_count() << std::endl;
      
      std::cout << std::endl;
      
      asSmartPointerBad(secSha);                                              // (6)
      std::cout << "*secSha: " << *secSha << std::endl;
      
      std::cout << std::endl;
      
    }
    

     

    I will start with the excellent case for a std::shared_ptr. The reference counter in line (1) is two because I used the shared pointer firSha to copy-initialized secSha. Let’s look closer at the invocation of the function asSmartPointerGood (2).  First (3), the reference count of shr is 2 and then becomes 1 in line (4).  What happened in line (5)? I reset shr to the new resource: new int(2011). Consequentially, the shared pointer firSha and secSha are immediately shared owners of different resources. You can observe the behavior in the screenshot.

    lifetimeSemantic

    If you invoke reset on a shared pointer, magic happens under the hood.

    • If you invoke reset without an argument, the reference counter will be decreased by one.
    • If you invoke reset with an argument and the reference counter was at least 2, you will get two independent shared pointer owning different resources. This is a kind of deep copy of shared pointers.
    • The resource will be released if you invoke reset with or without an argument and the reference counter becomes 0.

    This magic is not necessary if you are only interested in the underlying resource of the shared pointer; therefore, a pointer or a reference is the right parameter for the function asSmartPointerBad (6).

    Further information

    Look also at a recent post by Bartek F. about a situation where weak_ptr prevents full memory cleanup: How a weak_ptr might prevent full memory cleanup of a managed object.

    What’s next?

    There are six rules left for passing smart pointers to functions. So you know what I will write about in my next post.

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, Philipp Lenk, Hobsbawm, and Charles-Jianye Chen.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Seminars

    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    Standard Seminars (English/German)

    Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

    • C++ – The Core Language
    • C++ – The Standard Library
    • C++ – Compact
    • C++11 and C++14
    • Concurrency with Modern C++
    • Design Pattern and Architectural Pattern with C++
    • Embedded Programming with Modern C++
    • Generic Programming (Templates) with C++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    Modernes C++ Mentoring,

     

     

    2 replies
    1. خرید بهترین عطر مردانه
      خرید بهترین عطر مردانه says:

      Do you mind if I quote a couple of your posts as long as I provide credit and sources back to your blog?
      My blog site is in the exact same area of interest as yours and my visitors would really benefit from a
      lot of the information you present here. Please let
      me know if this alright with you. Thanks a lot!

      Reply

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *