Hands-On Design Patterns with C++
eBook - ePub

Hands-On Design Patterns with C++

Solve common C++ problems with modern design patterns and build robust applications

Fedor G. Pikus

  1. 512 Seiten
  2. English
  3. ePUB (handyfreundlich)
  4. Über iOS und Android verfügbar
eBook - ePub

Hands-On Design Patterns with C++

Solve common C++ problems with modern design patterns and build robust applications

Fedor G. Pikus

Angaben zum Buch
Buchvorschau
Inhaltsverzeichnis
Quellenangaben

Über dieses Buch

A comprehensive guide with extensive coverage on concepts such as OOP, functional programming, generic programming, and STL along with the latest features of C++

Key Features

  • Delve into the core patterns and components of C++ in order to master application design
  • Learn tricks, techniques, and best practices to solve common design and architectural challenges
  • Understand the limitation imposed by C++ and how to solve them using design patterns

Book Description

C++ is a general-purpose programming language designed with the goals of efficiency, performance, and flexibility in mind. Design patterns are commonly accepted solutions to well-recognized design problems. In essence, they are a library of reusable components, only for software architecture, and not for a concrete implementation.

The focus of this book is on the design patterns that naturally lend themselves to the needs of a C++ programmer, and on the patterns that uniquely benefit from the features of C++, in particular, the generic programming. Armed with the knowledge of these patterns, you will spend less time searching for a solution to a common problem and be familiar with the solutions developed from experience, as well as their advantages and drawbacks. The other use of design patterns is as a concise and an efficient way to communicate. A pattern is a familiar and instantly recognizable solution to specific problem; through its use, sometimes with a single line of code, we can convey a considerable amount of information. The code conveys: "This is the problem we are facing, these are additional considerations that are most important in our case; hence, the following well-known solution was chosen."

By the end of this book, you will have gained a comprehensive understanding of design patterns to create robust, reusable, and maintainable code.

What you will learn

  • Recognize the most common design patterns used in C++
  • Understand how to use C++ generic programming to solve common design problems
  • Explore the most powerful C++ idioms, their strengths, and drawbacks
  • Rediscover how to use popular C++ idioms with generic programming
  • Understand the impact of design patterns on the program's performance

Who this book is for

This book is for experienced C++ developers and programmers who wish to learn about software design patterns and principles and apply them to create robust, reusable, and easily maintainable apps.

Häufig gestellte Fragen

Wie kann ich mein Abo kündigen?
Gehe einfach zum Kontobereich in den Einstellungen und klicke auf „Abo kündigen“ – ganz einfach. Nachdem du gekündigt hast, bleibt deine Mitgliedschaft für den verbleibenden Abozeitraum, den du bereits bezahlt hast, aktiv. Mehr Informationen hier.
(Wie) Kann ich Bücher herunterladen?
Derzeit stehen all unsere auf Mobilgeräte reagierenden ePub-Bücher zum Download über die App zur Verfügung. Die meisten unserer PDFs stehen ebenfalls zum Download bereit; wir arbeiten daran, auch die übrigen PDFs zum Download anzubieten, bei denen dies aktuell noch nicht möglich ist. Weitere Informationen hier.
Welcher Unterschied besteht bei den Preisen zwischen den Aboplänen?
Mit beiden Aboplänen erhältst du vollen Zugang zur Bibliothek und allen Funktionen von Perlego. Die einzigen Unterschiede bestehen im Preis und dem Abozeitraum: Mit dem Jahresabo sparst du auf 12 Monate gerechnet im Vergleich zum Monatsabo rund 30 %.
Was ist Perlego?
Wir sind ein Online-Abodienst für Lehrbücher, bei dem du für weniger als den Preis eines einzelnen Buches pro Monat Zugang zu einer ganzen Online-Bibliothek erhältst. Mit über 1 Million Büchern zu über 1.000 verschiedenen Themen haben wir bestimmt alles, was du brauchst! Weitere Informationen hier.
Unterstützt Perlego Text-zu-Sprache?
Achte auf das Symbol zum Vorlesen in deinem nächsten Buch, um zu sehen, ob du es dir auch anhören kannst. Bei diesem Tool wird dir Text laut vorgelesen, wobei der Text beim Vorlesen auch grafisch hervorgehoben wird. Du kannst das Vorlesen jederzeit anhalten, beschleunigen und verlangsamen. Weitere Informationen hier.
Ist Hands-On Design Patterns with C++ als Online-PDF/ePub verfügbar?
Ja, du hast Zugang zu Hands-On Design Patterns with C++ von Fedor G. Pikus im PDF- und/oder ePub-Format sowie zu anderen beliebten Büchern aus Ciencia de la computación & Programación en C++. Aus unserem Katalog stehen dir über 1 Million Bücher zur Verfügung.

Information

Policy-Based Design

Policy-based design is one of the most well-known C++ patterns. Since the introduction of the standard template library in 1998, few new ideas have been more influential on the way we design C++ programs than the invention of policy-based design.
A policy-based design is all about flexibility, extensibility, and customization. It is a way to design software that can evolve, and can be adapted to the changing needs, some of which could not even be anticipated at the time when the initial design was conceived. A well-designed policy-based system can remain unchanged at the structural level for many years, and serve the changing needs and new requirements without compromise. Unfortunately, it is also a way to build software that could do all of those things if only there was someone who could figure out how it works. The aim of this chapter is to teach the reader to understand and design the systems of the former kind while avoiding the excesses that lead to the disasters of the latter one.
The following topics will be covered in this chapter:
  • Strategy pattern and policy-based design
  • Compile time policies in C++
  • Implementations of policy-based classes
  • Guidelines for the use of policies

Technical requirements

The example code for this chapter can be found at the following GitHub link: https://github.com/PacktPublishing/Hands-On-Design-Patterns-with-CPP/tree/master/Chapter16.

Strategy pattern and policy-based design

The classic Strategy pattern is a behavioral design pattern that enables the runtime selection of a specific algorithm for a particular behavior, usually from a predefined family of algorithms. This pattern is also known as the policy pattern; the name predates its application to the generic programming in C++. The aim of the Strategy pattern is to allow for more flexibility of the design: in the classic object-oriented Strategy pattern, the decision about which specific algorithm to use is deferred until runtime.
As is the case with many classic patterns, the generic programming in C++ applies the same approach to algorithm selection at compile time—it allows for compile-time customization of specific aspects of the system behavior by selecting from a family of related, compatible algorithms. We will now learn the basics of implementing classes with policies in C++, then proceed to study more complex and varied approaches to policy-based design.

Foundations of policy-based design

The Strategy pattern should be considered whenever we design a system that does certain operations, but the exact implementation of these operations is uncertain, varied, or can change after the system is implemented—in other words, when we know the answer to what the system must do, but not how. Similarly, the compile-time strategy, or a policy, is a way to implement a class that has a specific function (what), but there is more than one way to implement that function (how).
Throughout this chapter, we will, to illustrate different ways to use policies, design a smart pointer class. A smart pointer has many other required and optional features besides policies, and we will not cover all of them—for a complete implementation of a smart pointer, the reader is referred to such examples as the C++ standard smart pointers (unique_ptr and shared_ptr), Boost smart pointers, or the Loki smart pointer (http://loki-lib.sourceforge.net/). The material presented in this chapter will help the reader to understand the choices made by the implementers of these libraries, as well as how to design their own policy-based classes.
A very minimal initial implementation of a smart pointer may look like this:
template <typename T>
class SmartPtr {
public:
explicit SmartPtr(T* p = nullptr)
: p_(p) {}
~SmartPtr() {
delete p_;
}
T* operator->() { return p_; }
const T* operator->() const { return p_; }
T& operator*() { return *p_; }
const T& operator*() const { return *p_; }
private:
T* p_;
SmartPtr(const SmartPtr&) = delete;
SmartPtr& operator=(const SmartPtr&) = delete;
};
This pointer has a constructor from the raw pointer of the same type and the usual (for a pointer) operators, that is, * and ->. The most interesting part here is the destructor—when the pointer is destroyed, it automatically deletes the object as well (it is not necessary to check the pointer for the null value before deleting it; the operator delete is required to accept a null pointer and do nothing). It follows, therefore, that the expected use of this smart pointer is as follows:
Class C { ..... };
{
SmartPtr<C> p(new C);
..... use p .....
} // Object *p is deleted automatically
This is a basic example of the RAII class—the RAII object—the smart pointer, in our case—owns the resource (the constructed object) and releases (deletes) it when the owning object itself is deleted. The common applications, which were considered in detail in Chapter 5, A Comprehensive Look at RAII, focus on ensuring that the object that was constructed in the scope is deleted when the program exits this scope, no matter how the latter is accomplished (for example, if an exception is thrown somewhere in the middle of the code, the RAII destructor guarantees that the object is destroyed).
Two more member functions of the smart pointer are noted, not for their implementation, but for their absence—the pointer is made non-copyable as both its copy constructor and the assignment operator are disabled. This detail, which is sometimes overlooked, is of crucial importance for any RAII class—since the destructor of the pointer deletes the owned object, there should never be two pointers that point to, and will attempt to delete, the same object.
The pointer we have here is functional, but the implementation is constraining. In particular, it can own and delete only an object that was constructed with the standard operator new, and only a single object. While it could capture a pointer that was obtained from a custom operator new, or a pointer to an array of elements, it does not properly delete such objects.
We could implement a different smart pointer for objects that are created on a user-defined heap, and another one for objects that are created in client-managed memory, and so on, one for every type of object construction with its corresponding way of deletion. Most of the code for these pointers would be duplicated—they are all pointers, and the entire pointer-like API will have to be copied into every class. We can observe that all of these different classes are, fundamentally, of the same kind—the answer to the question what is this type? is always the same—it's a smart pointer. The only difference is in how the deletion is implemented. This common intent with a difference in one particular aspect of the behavior suggests the use of the Strategy pattern. We can implement a more general smart pointer where the details of how to handle the deletion of the object are delegates to one of any number of deletion policies:
template <typename T, typename DeletionPolicy>
class SmartPtr {
public:
explicit SmartPtr(
T* p = nullptr,
const DeletionPolicy& deletion_policy = DeletionPolicy()
) : p_(p),
deletion_policy_(deletion_policy)
{}
~SmartPtr() {
deletion_policy_(p_);
}
T* operator->() { return p_; }
const T* operator->() const { return p_; }
T& operator*() { return *p_; }
const T& operator*() const { return *p_; }
private:
T* p_;
DeletionPolicy deletion_policy_;
SmartPtr(const SmartPtr&) = delete;
SmartPtr& operator=(const SmartPtr&) = delete;
};
The deletion policy is an additional template parameter, and an object of the type of the deletion policy is passed to the constructor of the smart pointer (by default, such an object is default-constructed). The deletion policy object is stored in the smart pointer and is used in its destructor to delete the object that's being pointed to by the pointer.
The only requirement on the deletion policy type is that it should be callable—the policy is invoked, just like a function with one argument, and the pointer to the object that must be deleted. For example, the behavior of our original pointer that called operator delete on the object can be replicated with the following deletion policy:
template <typename T>
struct DeleteByOperator {
void operator()(T* p) const {
delete p;
}
};
To use this policy, we must specify its type when constructing the smart pointer, and, optionally, pass an object of this type to the constructor, although in this case, the default constructed object will work fine:
class C { ..... };
SmartPtr<C, DeleteByOperator<C>> p(new C);
If the deletion policy does not match the object type, a syntax error will be reported for the invalid call to operator().
Other deletion policies are needed for objects that were allocated in different ways. For example, if an object is created on a user-given heap object whose interface includes the member functions allocate() and deallocate() to, respectively, allocate and free memory, we can use the following heap deletion policy:
template <typename T>
struct DeleteHeap {
explicit DeleteHeap(Heap& heap)
: heap_(heap) {}
void operator()(T* p) const {
p->~T();
heap_.deallocate(p);
}
private:
Heap& heap_;
};
On the other hand, if an object is constructed in some memory that is managed separately by the caller, then only the destructor of the object needs to be called:
template <typename T>
struct DeleteDestructorOnly {
void operator()(T* p) const {
p->~T();
}
};
We mentioned earlier that, because the policy is used as a callable entity, deletion_policy_(p_), it can be of any type that can be called like a function. That includes the actual function:
typedef void (*delete_int_t)(int*);
void delete_int(int* p) { delete p; }
SmartPtr<int, delete_int_t> p(new int(42), delete_int);
A template instantiation is also a function and can be used in the same way:
template <typename T> void delete_T(T* p) { delete p; }
SmartPtr<int, delete_int_t> p(new int(42), delete_T<int>);
Of all the possible deletion policies, one is often the most commonly used. In most programs, it will likely be deletion by the default operator delete function. If this is so, it makes sense to avoid specifying this one policy every time it's used and make it the default:
template <typename T, typename DeletionPolicy = DeleteByOperator<T>>
class SmartPtr {
.....
};
Now, our policy-based smart pointer can be used in exactly the same way as the original version, with only one deletion option:
SmartPtr<C> p(new C);
Here, the second template parameter is left to its default value, DeleteByOperator<C>, and a default constructed object of this type is passed to the constructor as the default second argument.
At this point, we must caution the reader against a subtle mistake that could be made when implementing such policy-based classes. Note that the policy object is captured in the constructor of the smart pointer by a const reference:
explicit SmartPtr(
T* p = nullptr,
const DeletionPolicy& deletion_policy = DeletionPolicy());
The const reference here is important since a non-const reference cannot be bound to a temporary object (we will consider the r-value references later in this section). However, the policy is stored in the object itself by value, and, thus, a copy of the policy object must be made:
template <typename T, typename DeletionPolicy = DeleteByOperator<T>>
class SmartPtr {
.....
private:
DeletionPolicy deletion_policy_;
};
It may be tempting to avoid the copy and capture the policy by re...

Inhaltsverzeichnis