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 Deriveddeclared before, is not instantiated by the compiler until it is actually called , which happens Derivedafter 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 CRTPcall it "simulated dynamic binding".
This pattern is widely used Windows ATLin libraries WTLlike , and Boost.Iterator, Boost.Pythonor , Boost.Serializationetc.
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 overriddendata 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 (VTBLStructure and method lookup, multiple inheritance mechanism) and other costs. But CRTPthe 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 accessorclass on top of the derived class. Obviously, accessorthe class has the right to access the protected functions of the derived class; if the base class has the right to access accessorthe class, it can indirectly call the protected members of the derived class. This method is boostused by multiple libraries such as: Boost.Pythonin def_visitor_accessand 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 clonevirtual 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 Javawith properties in , which can be achieved by virtual inheritance in :finallyC++

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 var2will 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_ptrclasses encapsulate pointers or resources that can be shared . A shared object cannot directly pass its own raw pointer ( raw pointer) thisto std::shared_ptra 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`` APIa class template facility is provided std::enable_shared_from_thisthat contains member functions shared_from_thisallowing thisthe creation of an std::shared_ptrobject 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 thispointers in the base class implementation.

struct Foo : SomeBase<Foo>
{
	...
}

For example, assume SomeBasethat each inheritor of implements begin()/end() the pairs needed for iteration. So how would you SomeBaseiterate over the object's members? Intuition suggests that you can't do this because the interface SomeBaseisn't provided per se begin()/end(). CRTPHowever, you can actually thisconvert 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

insert image description here

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 FooBarBaztype.

Attributes

A property ( property , usually private) is just a combination of fields and getterand 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 propertyin 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)

insert image description here

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

Guess you like

Origin blog.csdn.net/MMTS_yang/article/details/130493786