Modern C++: A Closer Look at Smart Pointers | by Joseph Robinson, PhD | Apr, 2022

Efficient C++

The sequel to “Understanding Good Pointers.”

Picture by Chris Ried on Unsplash
· Recap
C++ Smart Pointers
· Toy Class
· In a nutshell
Why unique pointers
Why did auto_ptr not work?
Issues
Move Semantics
What is std::unique_ptr ?
Unique Ownership of unique pointer
Creating an empty unique_ptr object
Check if a unique_ptr<> is empty
Creating a unique_ptr object with a raw pointer
Resetting a unique_ptr
Transferring the ownership of the unique_ptr object
Releasing the associated raw pointer
· Source Code
· Source Code II
· References

RAII: Useful resource Acquisition Is Initialization, a C++ programming philosophy that makes use of wrapper courses to deal with useful resource administration — an idea manifested in good pointers. We are going to proceed our exploration of the subject from the place we left off (c.f. Part I). Typically, to progress most successfully, it prices a number of steps again in alternate for a lot of steps ahead later. Therefore, our recap that follows doesn’t solely summarize the teachings realized prior — further insights are offered by increasing our understanding of the primary version of good pointers (e.g., auto_ptr and its shortcomings that have been later improved by introducing the unique_ptr). Allow us to start!

C++ Good Pointers

A good pointer is an object that shops a pointer to a heap-allocated object.

  • A sensible pointer seems to be and behaves like a daily C++ pointer
  • By overloading *, ->, [], and so on.
  • These can assist you handle reminiscence.
  • The good pointer will delete the pointed-to object on the proper time, together with invoking the thing’s destructor.
  • When that is determined by what sort of good pointer you employ
  • With the proper use of good pointers, you not have to recollect when to delete sources allotted by way of new!

As talked about in a previous put up, there are good pointers offered as a part of the fashionable C++ normal library: unique_ptr, shared_ptr, and weak_ptr.

We are going to subsequent look nearer at a unique_ptr: an enchancment over the now deprecated auto_ptr. See Understanding Smart Pointers in C++ for an introduction to every; proceed onward to see precisely how unique_ptr improves auto_ptr.

Right here is the toy class used all through the tutorial. Source Code gives the code demonstrated all through.

Utilizing objects to handle sources:

  • Stop useful resource leaks, use RAII that purchase sources of their constructor and launch them of their destructor
  • Copying a useful resource is among the many challenges coders should ponder with

Consider carefully about copying habits in resource-managing courses

  1. Prohibit copying
  2. A reference counts the underlying useful resource
  3. Copy the underlying useful resource
  4. Switch possession of the underlying useful resource
  • Copying an RAII object entails copying the useful resource it manages, so the copying habits of the useful resource determines the copying habits of the RAII object.
  • Widespread RAII class copying behaviors disallow copying performing reference counting, however different behaviors are doable.

Why distinctive pointers

Packages typically are constructs of assorted branching and performance calls pushed on stack reminiscence. Therefore, many potential methods a program could move and even exit. Due to this fact, it’s straightforward to neglect to name delete on every time an object will get instantiated by way of new. In contrast to the non-deterministic Rubbish Collector utilized in JAVA, C++ gives a extra elegant, predictable answer by way of good pointers. C++98 launched the auto_ptr, paving the best way for the unique_ptr per C++11 [2]. The advantages are as follows:

  • unique_ptr will delete its pointer when it falls out of scope
  • Thus, a unique_ptr additionally helps with exception security
  • unique_ptr has disabled its copy constructor and task operator
You can’t copy a ‘unique_ptr' to keep up uniqueness or possession.

Why did auto_ptr not work?

Launched in C++98 and eliminated in C++17: the primary try at good pointers, which strikes semantics as its task and duplicate constructor.

Points

1. Passing a operate by worth: by transferring the possession, the auto_ptr assumes that the brand new pointer takes the place of the assignee, whereas the opposite is ready to null. Therefore, the pointer within the scope of the operate name is null upon coming back from the operate to which it was transferred.

2. std::auto_ptr all the time deletes its contents utilizing non-array delete.

3. Doesn’t play good with many different courses in the usual library, together with most containers and algorithms.

Transfer Semantics

In C++11, the idea of “transfer” was formally outlined, and “transfer semantics” have been added to the language to distinguish copying from transferring correctly.

Therefore, in C++11, std::auto_ptr has been changed by a bunch of different sorts of “move-aware” good pointers: std::unique_ptr, std::weak_ptr, and std::shared_ptr. We’ll additionally discover the 2 hottest: unique_ptr (a direct substitute for auto_ptr) and shared_ptr. unique_ptr is roofed on this weblog, whereas share_ptr would be the subsequent!

What’s std::unique_ptr ?

unique_ptr is likely one of the Good pointer implementations offered by c++11 to stop reminiscence leaks. A unique_ptr object wraps round a uncooked pointer and is chargeable for its lifetime. When this object is destructed, then in its destructor, it deletes the related uncooked pointer.

unique_ptr has -> and * operator overloaded to function as a standard pointer.

See Source Code II.

unique_ptr<MyClass> object myPtr accepts a uncooked pointer as arguments. This object will exit of scope when the operate exits, invoking its destructor. In its destructor unique_ptr object, myPtr deletes the related uncooked pointer.

The destructor of myPtr will all the time be referred to as if the operate is exited usually or not (i.e., as a result of some exception). Therefore, the uncooked pointer will all the time get deleted and stop the reminiscence leak.

Distinctive Possession of distinctive pointer

A unique_ptr is all the time the distinctive proprietor of the related uncooked pointer. We cannot copy a unique_ptr object; it’s only movable.

As every unique_ptr is the only proprietor of a uncooked pointer; it straight deletes the related pointer in its destructor. There is no such thing as a want for reference counting; subsequently, it is extremely mild.

Creating an empty unique_ptr object

Let’s create a empty unique_ptr<int> object i.e.

// Empty unique_ptr objectstd::unique_ptr<int> ptr1;

ptr1 has no uncooked pointer related to it. Therefore it’s empty.

Test if a unique_ptr<> is empty

There are two methods to test if a unique_ptr<> object is empty or it has a uncooked pointer related to it.

// Test if distinctive pointer object is emptyif(!ptr1)    std::cout<<”ptr1 is empty”<<std::endl;

// Test if distinctive pointer object is emptyif(ptr1 == nullptr)    std::cout<<”ptr1 is empty”<<std::endl;

Making a unique_ptr object with a uncooked pointer

To create a unique_ptr<> that’s nonempty, go the uncooked pointer in its constructor whereas creating the thing, i.e.,

We cannot create a unique_ptr<> object by way of the task. In any other case, it should trigger a compile error.

// std::unique_ptr<MyClass> myPtr2 = new MyClass(); // Compile Error

Resetting a unique_ptr

Calling reset() operate on a unique_ptr<> will reset it, i.e., it should delete the related uncooked pointer and make unique_ptr<> object empty i.e.

unique_ptr just isn’t copyable.

As unique_ptr<> just isn’t copyable, solely movable. Therefore we cannot create a replica of a unique_ptr object by way of a replica constructor or task operator.

Each copy constructor and task operator are deleted in unique_ptr<> class.

Transferring the possession of the unique_ptr object

We can not copy a unique_ptr object, however we will transfer them. It means a unique_ptr can switch the possession of the related uncooked pointer to a different unique_ptr. Let’s perceive by an instance,

Create a unique_ptr object (see traces 1–2 within the code pattern above):

myPtr2 just isn’t empty.

Now switch the possession of the related pointer of MyClass to a brand new unique_ptr object i.e.

std::transfer() convert myPtr2 to an RValue Reference. In order that transfer constructor of unique_ptr is invoked, and the related uncooked pointer may be transferred to myPtr4.

myPtr2 will likely be empty after transferring the possession of its uncooked pointer to myPtr4.

Releasing the related uncooked pointer

Calling launch() on the unique_ptr object will launch the possession of the related uncooked pointer from the thing, and it returns the uncooked pointer.

ptr1 is empty
ptr1 is empty
MyClass::Constructor
myPtr just isn’t empty
23
Reset the myPtr
MyClass::Destructor
myPtr is empty
MyClass::Constructor
myPtr3 just isn’t empty
myPtr2 is empty
myPtr4 just isn’t empty
55
MyClass::Destructor
MyClass::Constructor
myPtr5 just isn’t empty
myPtr5 is empty
55
MyClass::Destructor

[1] S. Meyers. “Efficient C++: 55 Particular Methods to Enhance Your Packages and Designs.” Addison-Wesley Skilled (Third Version), 2005.

More Posts