Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
C++ System Programming Cookbook

You're reading from   C++ System Programming Cookbook Practical recipes for Linux system-level programming using the latest C++ features

Arrow left icon
Product type Paperback
Published in Feb 2020
Publisher Packt
ISBN-13 9781838646554
Length 292 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Onorato Vaticone Onorato Vaticone
Author Profile Icon Onorato Vaticone
Onorato Vaticone
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Getting Started with System Programming 2. Revisiting C++ FREE CHAPTER 3. Dealing with Processes and Threads 4. Deep Dive into Memory Management 5. Using Mutexes, Semaphores, and Condition Variables 6. Pipes, First-In First-Out (FIFO), Message Queues, and Shared Memory 7. Network Programming 8. Dealing with Console I/O and Files 9. Dealing with Time Interfaces 10. Managing Signals 11. Scheduling 12. Other Books You May Enjoy

Smart pointers – unique_ptr and shared_ptr

This recipe will show the basic usage of unique_ptr and shared_ptr. These smart pointers are the main helpers for programmers who don't want to deal with memory deallocation manually. Once you've learned how to use them properly, this will save headaches and nights of debugging sessions.

How to do it...

In this section, we'll look at the basic use of two smart pointers, std::unique_ptr and std::shared_ptr:

  1. Let's develop a unique_ptr example by developing the following class:
#include <iostream>
#include <memory>
class CruiseControl
{
public:
CruiseControl()
{
std::cout << "CruiseControl object created" << std::endl;
};
~CruiseControl()
{
std::cout << "CruiseControl object destroyed" << std::endl;
}
void increaseSpeedTo(int speed)
{
std::cout << "Speed at " << speed << std::endl;
};
};
  1. Now, let's develop a main class by calling the preceding class:
int main ()
{
std::cout << "unique_ptr test started" << std::endl;
std::unique_ptr<CruiseControl> cruiseControl =
std::make_unique<CruiseControl>();
cruiseControl->increaseSpeedTo(12);
std::cout << "unique_ptr test finished" << std::endl;
}
  1. Let's compile g++ unique_ptr_01.cpp.
  2. Another example with unique_ptr shows its behavior with arrays. Let's reuse the same class (CruiseControl):
int main ()
{
std::cout << "unique_ptr test started" << std::endl;
std::unique_ptr<CruiseControl[]> cruiseControl =
std::make_unique<CruiseControl[]>(3);
cruiseControl[1].increaseSpeedTo(12);
std::cout << "unique_ptr test finished" << std::endl;
}
  1. Let's see std::shared_ptr in action with a small program:
#include <iostream>
#include <memory>
class CruiseControl
{
public:
CruiseControl()
{
std::cout << "CruiseControl object created" << std::endl;
};
~CruiseControl()
{
std::cout << "CruiseControl object destroyed" << std::endl;
}
void increaseSpeedTo(int speed)
{
std::cout << "Speed at " << speed << std::endl;
};
};

main looks like this:

int main ()
{
std::cout << "shared_ptr test started" << std::endl;
std::shared_ptr<CruiseControl> cruiseControlMaster(nullptr);
{
std::shared_ptr<CruiseControl> cruiseControlSlave =
std::make_shared<CruiseControl>();
cruiseControlMaster = cruiseControlSlave;
}
std::cout << "shared_ptr test finished" << std::endl;
}

The How it works... section will describe these three programs in detail.

How it works...

By running the first unique_ptr program, that is, ./a.out, we get the following output:

unique_ptr is a smart pointer that embodies the concept of unique ownership. Unique ownership, simply put, means that there is one and only one variable that can own a pointer. The first consequence of this concept is that the copy operator is not allowed on two unique pointer variables. Just move is allowed, where the ownership is transferred from one variable to another. The executable that was run shows that the object is deallocated at the end of the current scope (in this case, the main function): CruiseControl object destroyed. The fact that the developer doesn't need to bother remembering to call delete when needed, but still keep control over memory, is one of the main advantages of C++ over garbage collector-based languages.

In the second unique_ptr example, with arrays, there are three objects of the CruiseControl type that have been allocated and then released. For this, the output is as follows:

    The third example shows usage of shared_ptr. The output of the program is as follows:

    The shared_ptr smart pointer represents the concept that an object is being pointed at (that is, by the owner) by more than one variable. In this case, we're talking about shared ownership. It is clear that the rules are different from the unique_ptr case. An object cannot be released until at least one variable is using it. In this example, we defined a cruiseControlMaster variable pointing to nullptr. Then, we defined a block and in that block, we defined another variable: cruiseControlSlave. So far, so good! Then, still inside the block, we assigned the cruiseControlSlave pointer to cruiseControlMaster. At this point, the object allocated has two pointers: cruiseControlMaster and cruiseControlSlave. When this block is closed, the cruiseControlSlave destructor is called but the object is not freed as it is still used by another one: cruiseControlMaster! When the program finishes, we see the shared_ptr test finished log and immediately after the cruiseControlMaster, as it is the only one pointing to the CruiseControl object release, the object and then the constructor is called, as reported in the CruiseControl object destroyed log.

    Clearly, the shared_ptr data type has a concept of reference counting to keep track of the number of pointers. These references are increased during the constructors (not always; the move constructor isn't) and the copy assignment operator and decreased in the destructors.

    Can the reference counting variable be safely increased and decreased? The pointers to the same object might be in different threads, so manipulating this variable might be an issue. This is not an issue as the reference counting variable is atomically managed (that is, it is an atomic variable).

    One last point about the size. unique_ptr is as big as a raw pointer, whereas shared_ptr is typically double the size of unique_ptr because of the reference counting variable.

    There's more...

    I strongly suggest always using std::make_unique and std::make_shared. Their usage removes code duplication and improves exception safety. Want more details? shared_ptr.h (https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr.h) and shared_ptr_base.h (https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h) contain the GCC shared_ptr implementation so that we can see how reference counting is manipulated.

    See also

    The books Effective Modern C++ by Scott Meyers and The C++ Programming Language by Bjarne Stroustrup cover these topics in great detail.

    You have been reading a chapter from
    C++ System Programming Cookbook
    Published in: Feb 2020
    Publisher: Packt
    ISBN-13: 9781838646554
    Register for a free Packt account to unlock a world of extra content!
    A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
    Unlock this book and the full library FREE for 7 days
    Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
    Renews at $19.99/month. Cancel anytime