Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
roth_stephan_clean_c20_sustainable_software_development_patt.pdf
Скачиваний:
29
Добавлен:
27.03.2023
Размер:
7.26 Mб
Скачать

Chapter 5 Advanced Concepts of Modern C++

}catch (...) {

//Perform error handling here...

}

As it can be easily seen, no new or delete is required. If resource runs out of scope, which can happen at various points in this method, the wrapped instance of type ResourceType is deleted automatically through the destructor of ScopedResource.

But there is usually no need to reinvent the wheel and to implement such a wrapper, which is also called a smart pointer.

Smart Pointers

Since C++11, the Standard Library offers different, efficient smart-pointer-­ implementations for easy use. These pointers have been developed over a long period within the well-known Boost library project before they were introduced into the C++ standard, and can be regarded as foolproof as possible. Smart pointers reduce the likelihood of memory leaks. Furthermore, their reference counter mechanism is designed to be thread-safe.

This section provides a brief overview.

Unique Ownership with std::unique_ptr<T>

The class template std::unique_ptr<T> (defined in the <memory> header) manages a pointer to an object of type T. As the name suggests, this smart pointer provides unique ownership, that is, an object can be owned by only one instance of std::unique_ptr<T> at a time, which is the main difference of the std::shared_ptr<T>, which is explained next. This also means that copy construction and copy assignment are not allowed.

Its use is pretty simple:

#include <memory>

class ResourceType { //...

};

//...

std::unique_ptr<ResourceType> resource1 { std::make_unique<ResourceType>() };

135

Chapter 5 Advanced Concepts of Modern C++

// ... or shorter with type deduction ...

auto resource2 { std::make_unique<ResourceType>() };

After this construction, resource can be used very much like a regular pointer to an instance of ResourceType. (std::make_unique<T> is explained in the section entitled “Avoid new and delete”). For example, you can use the * and -> operators for dereferencing:

resource->foo();

Of course, if resource runs out of scope, the contained instance of type ResourceType is freed safely. But the best part is that resource can be easily put into containers, for example, in a std::vector:

#include "ResourceType.h"

#include <memory> #include <vector>

using ResourceTypePtr = std::unique_ptr<ResourceType>; using ResourceVector = std::vector<ResourceTypePtr>;

//...

ResourceTypePtr resource { std::make_unique<ResourceType>() }; ResourceVector aCollectionOfResources; aCollectionOfResources.push_back(std::move(resource));

// IMPORTANT: At this point, the instance of 'resource' is empty!

Note that we ensure that std::vector::push_back() calls the move constructor and the move assignment operator of std::unique_ptr<T> (see the section about move semantics in the next chapter). As a consequence, resource does not manage an object anymore and is denoted as empty.

As mentioned, copy construction of std::unique_ptr<T> is not allowed. However, the exclusive ownership of the managed resource can be transferred to another instance of std::unique_ptr<T>, using move semantics (we will discuss move semantics in detail in a later section) in the following way:

std::unique_ptr<ResourceType> pointer1 { std::make_unique<ResourceType>() }; std::unique_ptr<ResourceType> pointer2; // pointer2 owns nothing yet

pointer2 = std::move(pointer1);

// Now pointer1 is empty, pointer2

 

is the new owner

136

Chapter 5 Advanced Concepts of Modern C++

Shared Ownership with std::shared_ptr<T>

Instances of class template std::shared_ptr<T> (defined in the <memory> header) can take ownership of a resource of type T and can share this ownership with other instances of std::shared_ptr<T>. In other words, the ownership for a single instance of type T, and thus the responsibility for its deletion, can be taken over by many shared owners.

std::shared_ptr<T> provides something like simple limited garbage collector functionality. The smart pointer’s implementation has a reference counter that monitors how many pointer instances owning the shared object still exist. It releases the managed resource if the last instance of the pointer is destroyed.

Figure 5-1 depicts a class diagram, as well as an object diagram. The lower area of the figure, where the object diagram can be seen, depicts a situation (snapshot) in a running system where three anonymous instances of the class Client share the same resource (:Resource) using three std::shared_ptr instances. The _M_use_count attribute represents the reference counter of std::shared_ptr.

137

Chapter 5 Advanced Concepts of Modern C++

Figure 5-1.  Three clients are sharing one resource through smart pointers

In contrast to the previously discussed std::unique_ptr<T>, std::shared_ptr<T> is of course copy-constructible as expected. But you can ensure that the managed resource is moved by using std::move<T>:

std::shared_ptr<ResourceType> pointer1 { std::make_shared<ResourceType>() }; std::shared_ptr<ResourceType> pointer2;

pointer2 = std::move(pointer1); // The reference count does not get modified, pointer1 is empty

In this case, the reference counter is not modified, but you must be careful when using the variable pointer1 after the move, because it is empty, that is, it holds a nullptr. Move semantics and the utility function std::move<T> are discussed in a later section.

138