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

Chapter 5 Advanced Concepts of Modern C++

Managing Resources

Managing resources is the bread-and-butter business of software developers. A multitude of miscellaneous resources must be regularly allocated, used, and returned after use. These include the following:

•\

Memory (either on the stack or on the heap)

•\

File handles that are required to access files (read/write) on hard disk

 

or other media

•\

Network connections (e.g., to a server, a database, etc.)

•\

Threads, locks, timers, and transactions

•\

Other operational system resources, like GDI handles on Windows

 

operating systems1

The proper handling of resources can be a tricky task. Consider the example in Listing 5-1.

Listing 5-1.  Dealing with a Resource That Was Allocated on the Heap

void doSomething() {

ResourceType* resource = new ResourceType(); try {

// ...do something with resource...

resource->foo();

}catch (...) { delete resource;

throw;

}

delete resource;

}

What’s the problem here? Perhaps you’ve noticed the two identical delete statements. The catch-all exception handling mechanism introduces at least two possible paths in our program. This also means that we have to ensure that the resource

1GDI stands for Graphics Device Interface. GDI is a core operating system component of Microsoft Windows and is responsible for representing graphical objects.

132

Chapter 5 Advanced Concepts of Modern C++

is freed in two places. Under normal circumstances such catch-all exception handlers are frowned upon. But in this case, we have no other chance than to catch all possible occurring exceptions here, because we must free the resource first, before we throw the exception object further to treat it elsewhere (e.g., at the call site of the function).

I this simplified example, we have only two paths. In real programs, significantly more execution paths can exist. The probability that one delete is forgotten is much higher. Any forgotten delete will result in a dangerous resource leakage.

Warning  Do not underestimate resource leaks! Resource leaks are a serious problem, particularly for long-lived processes, and for processes that rapidly allocate many resources without deallocating them after usage. If an

operating system has a lack of resources, this can lead to critical system states. Furthermore, resource leaks can be a security issue, because they can be exploited by assaulters during denial-of-service attacks.

The simplest solution for our small example could be that we allocate the resource on the stack, instead of allocating it on the heap, as shown in Listing 5-2.

Listing 5-2.  Much Easier: Dealing with a Resource on the Stack

void doSomething() { ResourceType resource;

// ...do something with resource...

resource.foo();

}

With this change the resource is safely removed in any case. But sometimes it is not possible to allocate everything on the stack, as we’ve discussed in the section “Don’t Pass or Return 0 (nullptr)” in Chapter 4. What about file handles, OS resources, etc.?

The central question is this: How can we guarantee that allocated resources are always freed?

133

Chapter 5 Advanced Concepts of Modern C++

Resource Acquisition Is Initialization (RAII)

Resource acquisition is initialization (RAII) is an idiom (see Chapter 9 about idioms) that can help cope with resources in a safe way. The idiom is also known as constructor acquires, destructor releases (CADRe) and scope-based resource management (SBRM).

RAII takes advantage of the symmetry of a class between its constructor and its corresponding destructor. We can allocate a resource in the constructor of a class, and we can deallocate it in the destructor. If we create such a class as a template, it can be used for different types of resources. See Listing 5-3.

Listing 5-3.  A Very Simple Class Template That Can Manage Several Types of Resources

template <typename RESTYPE> class ScopedResource final { public:

ScopedResource() { managedResource = new RESTYPE(); } ~ScopedResource() { delete managedResource; }

RESTYPE* operator->() const { return managedResource; } RESTYPE& operator*() const { return *managedResource; }

private:

RESTYPE* managedResource;

};

Now we can use the class template called ScopedResource, as shown in Listing 5-4.

Listing 5-4.  Using ScopedResource to Manage an Instance of ResourceType

#include "ScopedResource.h" #include "ResourceType.h"

void doSomething() {

ScopedResource<ResourceType> resource;

try {

// ...do something with resource...

resource->foo();

134