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

Chapter 5 Advanced Concepts of Modern C++

Avoid Explicit New and Delete

In a modern C++ program, when writing application code you should avoid calling new and delete explicitly. Why? Well, the simple and short explanation is this: new and delete increase complexity.

The more detailed answer is this: every time when it is inevitable to call new and delete, one has to deal with an exceptional, non-default situation, a situation that requires special treatment. To understand these exceptional cases, let’s take a look on the default cases—the situations any C++ developer should strive for.

Explicit calls of new and/or delete can be avoided using the following measures:

•\ Use allocations on the stack wherever possible. Allocations on the stack are simple (remember the KISS principle discussed in Chapter 3) and safe. It’s impossible to leak any of that memory that was allocated on the stack. The resource will be destroyed once it goes out of scope. You can even return the object from a function by value, thus transferring its contents to the calling function.

•\ To allocate a resource on the heap, use “make functions.” Use std::make_unique<T> or std::make_shared<T> to instantiate the resource and wrap it immediately into a manager object that takes care of the resource, a smart pointer.

•\ Use containers (Standard Library, Boost, or others) wherever appropriate. These well-designed containers are bullet-proof and manage the storage space for their elements in the correct manner. Instead, in the case of self-developed data structures and sequences, you are forced to implement the entire storage management on your own, which can be a complex and error-prone task.

•\ Provide wrappers for resources from proprietary third-party libraries that require a specific memory management (see the next section).

Managing Proprietary Resources

As mentioned in the introduction to this section about resource management, sometimes other resources need to be managed that are not allocated or deallocated on the heap using the default new or delete operator. Examples of such kinds of resources

144

Chapter 5 Advanced Concepts of Modern C++

are opened files from a file system, database connections, a dynamically loaded module (e.g., a Dynamic Link Library [DLL] on Windows operating systems), or platform-specific objects of a graphical user interface (e.g., Windows, Buttons, Text input fields, etc.).

Often these kinds of resources are managed through something that is called a handle. A handle is an abstract and unique reference to an operational system resource. On Windows, the data type HANDLE is used to define such handles. In fact, this data type is defined as follows in the WinNT.h header, a C-style header file that defines various Win32 API macros and types:

typedef void *HANDLE;

For instance, if you want to access a running Windows process with a certain process ID, you can retrieve a handle to this process using the Win32 API function called

OpenProcess().

#include <windows.h> // ...

const DWORD processId = 4711;

HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);

After you are finished with the handle, you have to close it by using the

CloseHandle() function:

BOOL success = CloseHandle(processHandle);

Hence, we have symmetry similar to the new operator and its corresponding delete operator. It should therefore also be possible to take advantage of the RAII idiom and use smart pointers for such resources. First, we just have to exchange the default deleter (which calls delete) by a custom deleter that calls CloseHandle():

#include <windows.h> // Windows API declarations

class Win32HandleCloser { public:

void operator()(HANDLE handle) const { if (handle != INVALID_HANDLE_VALUE) {

CloseHandle(handle);

}

}

};

145