Design Patterns - Basic Concepts
basic concept
Singular Recursive Template Pattern (CRTP)
Curiously recurring template pattern (CRTP) is C++
an idiom ( ) in template programming idiom
: use the derived class as the template parameter of the base class.
illustrate
The polymorphism of the static polymorphic
C++
language is originally implemented with virtual functions, which belongs to dynamic polymorphism . Andre proposed the singular recursive template pattern in "Modern C++ Design" , and called it static polymorphism (static polymorphism).
#include <iostream>
using namespace std;
template <typename Child>
struct Base {
void interface() {
static_cast<Child*>(this)->implementation(); }
static void static_func() {
Child::static_sub_func(); }
};
struct Derived : Base<Derived> {
void implementation() {
cout << "Derived implementation\n"; }
static void static_sub_func() {
cout << "child function " << endl; }
};
int main() {
Derived d;
d.interface(); // Prints "Derived implementation"
Base<Derived> m;
m.static_func();
}
operation result
Derived implementation
child function
Base class templates take advantage of the fact that their member function bodies (that is, the implementations of member functions) are not instantiated long after they are declared (actually only the member functions of the called template class are instantiated), and that derived class member function of (via type conversion).
In the above example, Base<Derived>::interface()
, although struct Derived
declared before, is not instantiated by the compiler until it is actually called , which happens Derived
after the declaration, when Derived::implementation()
the declaration is known.
This technique achieves an effect similar to virtual functions, and avoids the cost of dynamic polymorphism . Some people also CRTP
call it "simulated dynamic binding".
This pattern is widely used Windows ATL
in libraries WTL
like , and Boost.Iterator
, Boost.Python
or , Boost.Serialization
etc.
Considering a base class without virtual functions, other member functions that its member functions can call can only belong to the base class itself. When other classes are derived from this base class, the derived class inherits all overridden
data members and member functions of the base class that are not covered ( ). If the derived class calls a function** of an inherited base class, and the function calls other member functions, these member functions cannot be derived or overridden member functions in the derived class . In other words, the derived class cannot be seen in the base class. However, if the base class is used , the overridden function of the derived class can be selected to be calledCRTP
at compile time . This effect is equivalent to simulating virtual function calls at compile time but avoiding the size and call overhead of virtual functions (VTBL
Structure and method lookup, multiple inheritance mechanism) and other costs. But CRTP
the disadvantage is that dynamic binding cannot be made at runtime.
If the base class accesses the private or protected members of the derived class without using the virtual function mechanism, the base class needs to be declared as a friend of the derived class ( friend
). If a class has multiple base classes, it will be troublesome to declare that multiple base classes are friends. One solution is to derive a accessor
class on top of the derived class. Obviously, accessor
the class has the right to access the protected functions of the derived class; if the base class has the right to access accessor
the class, it can indirectly call the protected members of the derived class. This method is boost
used by multiple libraries such as: Boost.Python
in def_visitor_access
and Boost.Iterator的iterator_core_access
. The principle sample code is as follows:
template<class DerivedT> class Base {
private:
struct accessor : DerivedT {
// accessor类没有数据成员,只有一些静态成员函数
static int foo(DerivedT& derived) {
int (DerivedT::*fn)() = &DeriveT::do_foo; //获取DerivedT::do_foo的成员函数指针
return (derived.*fn)(); // 通过成员函数指针的函数调用
}
}; // accessor类仅是Base类的成员类型,而没有实例化为Base类的数据成员。
public:
DerivedT& derived() {
// 该成员函数返回派生类的实例的引用
return static_cast<DerivedT&>(*this);
}
int foo() {
// 该函数具体实现了业务功能
return accessor::foo( this->derived());
}
};
struct Derived : Base<Derived> {
// 派生类不需要任何特别的友元声明
protected:
int do_foo() {
// ... 具体实现
return 1;
}
};
example
Example 1: Object Counting
Statistics on the creation and destruction of instance objects of a class. can easily be implemented using CRTP
:
#include <iostream>
using namespace std;
template <typename T>
struct counter {
static int objects_created;
static int objects_alive;
counter() {
++objects_created;
++objects_alive;
}
counter(const counter&) {
++objects_created;
++objects_alive;
}
protected:
~counter() // 永远不应该通过这种类型的指针删除对象
{
--objects_alive;
}
};
template <typename T>
int counter<T>::objects_created(0);
template <typename T>
int counter<T>::objects_alive(0);
class X : counter<X> {
public:
X() {
cout << "x constructor" << endl; }
};
class Y : counter<Y> {
public:
Y() {
cout << "y constructor" << endl; }
~Y() {
cout << "y Desconstructor" << endl; }
};
int main() {
X x;
cout << "created " << counter<X>::objects_created << endl;
{
Y y;
cout << "created " << counter<Y>::objects_created << " alive "
<< counter<Y>::objects_alive << endl;
}
Y y1;
cout << "created " << counter<Y>::objects_created << " alive "
<< counter<Y>::objects_alive << endl;
return 0;
}
operation result
x constructor
created 1
y constructor
created 1 alive 1
y Desconstructor
y constructor
created 2 alive 1
y Desconstructor
Example 2: Polymorphic copy construction
When using polymorphism, it is often necessary to create a copy of the object based on the base class pointer. A common approach is to add clone
virtual functions in each derived class. Use CRTP
, you can avoid adding such a virtual function in the derived class.
#include <iostream>
using namespace std;
// 基类具有用于克隆的纯虚函数
class Shape {
public:
virtual ~Shape() {
}
virtual Shape *clone() const = 0;
virtual void printName() {
cout << "base Shape" << endl; }
};
// 这个CRTP类为Derived实现了clone()
template <typename Derived>
class Shape_CRTP : public Shape {
public:
virtual Shape *clone() const {
cout << "Shape_CRTP clone" << endl;
return new Derived(static_cast<Derived const &>(*this));
}
};
// 这个宏,确保正确的CRTP使用
#define Derive_Shape_CRTP(Type) class Type : public Shape_CRTP<Type>
// 每个派生类都继承Shape_CRTP而不是Shape
Derive_Shape_CRTP(Square){
void printName(){
cout << "Square" << endl;}};
Derive_Shape_CRTP(Circle){
void printName(){
cout << "Circle" << endl;}};
int main() {
Shape_CRTP<Square> baseSquare;
baseSquare.clone()->printName();
baseSquare.printName();
cout << endl;
Shape_CRTP<Circle> baseCircle;
baseCircle.clone()->printName();
baseCircle.printName();
return 0;
}
This allows by shapePtr->clone()
getting a copy of a square, circle or any other shape, which works like this:
Shape_CRTP clone
Square
base Shape
Shape_CRTP clone
Circle
base Shape
Example 3: Non-Derivable Classes
If a class does not want to be inherited, it is similar to the class Java
with properties in , which can be achieved by virtual inheritance in :finally
C++
template <typename T>
class MakeFinally {
private:
MakeFinally() {
} // 只有MakeFinally的友类才可以构造MakeFinally
~MakeFinally() {
}
friend T;
};
class MyClass : public virtual MakeFinally<MyClass> {
}; // MyClass是不可派生类
// 由于虚继承,所以D要直接负责构造MakeFinally类,从而导致编译报错,所以D作为派生类是不合法的。
class D : public MyClass {
};
// 另外,如果D类没有实例化对象,即没有被使用,实际上D类是被编译器忽略掉而不报错
int main() {
MyClass var1;
// D var2; //这一行编译将导致错误,因为D类的默认构造函数不合法
}
Open D var2
will report the following error
<source>: In function 'int main()':
<source>:15:7: error: use of deleted function 'D::D()'
15 | D var2; //这一行编译将导致错误,因为D类的默认构造函数不合法
| ^~~~
<source>:11:7: note: 'D::D()' is implicitly deleted because the default definition would be ill-formed:
11 | class D : public MyClass {
};
| ^
<source>:11:7: error: 'MakeFinally<T>::MakeFinally() [with T = MyClass]' is private within this context
<source>:4:5: note: declared private here
4 | MakeFinally() {
} // 只有MakeFinally的友类才可以构造MakeFinally
| ^~~~~~~~~~~
<source>:11:7: error: 'MakeFinally<T>::~MakeFinally() [with T = MyClass]' is private within this context
11 | class D : public MyClass {
};
| ^
<source>:5:5: note: declared private here
5 | ~MakeFinally() {
}
| ^
<source>:15:7: error: use of deleted function 'D::~D()'
15 | D var2; //这一行编译将导致错误,因为D类的默认构造函数不合法
| ^~~~
<source>:11:7: note: 'D::~D()' is implicitly deleted because the default definition would be ill-formed:
11 | class D : public MyClass {
};
| ^
<source>:11:7: error: 'MakeFinally<T>::~MakeFinally() [with T = MyClass]' is private within this context
<source>:5:5: note: declared private here
5 | ~MakeFinally() {
}
| ^
例子4:std::enable_shared_from_this
In C++
standard library header files <memory>
, std::shared_ptr
classes encapsulate pointers or resources that can be shared . A shared object cannot directly pass its own raw pointer ( raw pointer
) this
to std::shared_ptr
a container object (such as a std::vector
), because this would generate an additional shared pointer control block for the shared object . To this end, std::shared_ptr`` API
a class template facility is provided std::enable_shared_from_this
that contains member functions shared_from_this
allowing this
the creation of an std::shared_ptr
object from a class.
class mySharedClass:public std::enable_shared_from_this<mySharedClass>{
public:
// ...
};
int main()
{
std::vector<std::shared_ptr<mySharedClass>> spv;
spv.push_back(new mySharedClass());
std::shared_ptr<mySharedClass> p(new mySharedClass());
mySharedClass &c=*p;
spv.emplace_back(c.shared_from_this());
}
The inheritor passes itself as a template parameter to its base class. One reason is to facilitate access to typed this
pointers in the base class implementation.
struct Foo : SomeBase<Foo>
{
...
}
For example, assume SomeBase
that each inheritor of implements begin()/end()
the pairs needed for iteration. So how would you SomeBase
iterate over the object's members? Intuition suggests that you can't do this because the interface SomeBase
isn't provided per se begin()/end()
. CRTP
However, you can actually this
convert to a derived class type if you use :
template <typename Derived>
struct SomeBase
{
void foo()
{
for (auto& item : *static_cast<Derived*>(this))
{
...
}
}
}
Example 5 Simple tree traversal
#include <assert.h>
#include <iostream>
using namespace std;
struct TreeNode {
enum Kind {
RED, BLUE, YELLOW };
TreeNode(Kind kind_, TreeNode* left_ = NULL, TreeNode* right_ = NULL)
: kind(kind_), left(left_), right(right_) {
}
Kind kind;
TreeNode *left, *right;
};
template <typename Derived>
class GenericVisitor {
public:
void visit_preorder(TreeNode* node) {
if (node) {
dispatch_node(node);
visit_preorder(node->left);
visit_preorder(node->right);
}
}
void visit_inorder(TreeNode* node) {
if (node) {
visit_inorder(node->left);
dispatch_node(node);
visit_inorder(node->right);
}
}
void visit_postorder(TreeNode* node) {
if (node) {
visit_postorder(node->left);
visit_postorder(node->right);
dispatch_node(node);
}
}
void handle_RED(TreeNode* node) {
cout << "Generic handle RED\n"; }
void handle_BLUE(TreeNode* node) {
cout << "Generic handle BLUE\n"; }
void handle_YELLOW(TreeNode* node) {
cout << "Generic handle YELLOW\n"; }
private:
// Convenience method for CRTP
Derived& derived() {
return *static_cast<Derived*>(this); }
void dispatch_node(TreeNode* node) {
switch (node->kind) {
case TreeNode::RED:
derived().handle_RED(node);
break;
case TreeNode::BLUE:
derived().handle_BLUE(node);
break;
case TreeNode::YELLOW:
derived().handle_YELLOW(node);
break;
default:
assert(0);
}
}
};
class SpecialVisitor : public GenericVisitor<SpecialVisitor> {
public:
void handle_RED(TreeNode* node) {
cout << "RED is special\n"; }
};
int main() {
TreeNode node(TreeNode::RED);
node.left = new TreeNode(TreeNode::BLUE);
node.right = new TreeNode(TreeNode::YELLOW);
SpecialVisitor d;
// d.handle_RED(&node); // Prints "Derived implementation"
// d.handle_BLUE(&node);
cout << "visit_postorder " << endl;
d.visit_postorder(&node);
cout << "\nvisit_preorder " << endl;
d.visit_preorder(&node);
cout << "\nvisit_inorder\n";
d.visit_inorder(&node);
delete (node.left);
delete (node.right);
}
operation result:
visit_postorder
Generic handle BLUE
Generic handle YELLOW
RED is special
visit_preorder
RED is special
Generic handle BLUE
Generic handle YELLOW
visit_inorder
Generic handle BLUE
RED is special
Generic handle YELLOW
Example 6 Memory alignment during operator new memory management
mixed inheritance
In C++
, a class can be defined as inheriting from its own template parameters, for example:
template <typename T> struct Mixin : T
{
...
}
This approach is known as mixin inheritance and allows for hierarchical composition of types. For example, you could allow Foo\<Bar\<Baz>> x;
declaring a variable of a type that implements the traits of all three classes without actually constructing an entirely new FooBarBaz
type.
Attributes
A property ( property , usually private) is just a combination of fields and getter
and setter
. In the standard C++
, a property looks like this:
class Person
{
int age;
public:
int get_age() const { return age; }
void set_age(int value) { age = value; }
};
A large number of programming languages (eg, C#
, Kotlin
) internalize the concept of properties by adding them directly to the programming language. While C++
this is not done (and is unlikely to be done in the future), there is a non-standard declaration specifier called that you can use property
in most compilers ( ):MSVC、Clang、Intel
class Person
{
int age_;
public:
int get_age() const { return age_; }
void set_age(int value) { age_ = value; }
__declspec(property(get=get_age, put=set_age)) int age;
};
This can be used as follows:
Person person;
p.age = 20; // calls p.set_age(20)
SOLID Design Principles
SOLID
is an acronym that stands for the following design principles (and their abbreviations):
- Single Responsibility Principle (SRP)
- Open Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Injection Principle (DIP)
For details, please refer to the basic principles of design patterns
reference
[1] CRTP