C++那些事之Mixin惯用法

C++那些事之Mixin惯用法

大家好,我是光城,今天给大家分享C++那些事里面的一个惯用法:mixin

混合(Mixins)是Lisp中的一个概念。混合是类的一部分,意味着它旨在与其他类或混合组合在一起。常规独立类(例如Person)与混合的区别在于,混合模拟了一些小功能片段(例如打印或显示),并不用于独立使用。相反,它应该与需要此功能的其他类(例如Person)组合在一起。

因此,混合的目的是允许类似于多重继承的东西。

在C++汇总mixin的基本写法如下:

template <class Super>
class Mixin : public Super {
. /* mixin body */
};

或者

template<typename... Super>
class Mixin : public Super... {
  public:
    Mixin() :  Super...() {}
  // ...
};

注:本节的所有示例代码可从星球获取。

  • C++那些事之Mixin惯用法

  • 示例1:缩放与旋转

  • 示例2:redo与undo

  • 示例3:重复打印

  • 标准库

  • 参考

示例1:缩放与旋转

假设我们要对一个长方形/正方形进行缩放、旋转、添加边框等,这些操作都会影响其宽度与高度,我们可以使用mixin来实现。

首先,我们可以定义好正常形与长方形作为mixin的基类。

// 正方形类
class Square : public Shape {
public:
    Square(int sideLength) : sideLength(sideLength) {}
    int GetWidth() const override { return sideLength; }
    int GetHeight() const override { return sideLength; }
private:
    int sideLength;
};

// 长方形类
class Rectangle : public Shape {
public:
    Rectangle(int width, int height) : width(width), height(height) {}
    int GetWidth() const override { return width; }
    int GetHeight() const override { return height; }
private:
    int width;
    int height;
};

随后使用mixin子类去拓展功能,例如:一个缩放的mixin我们可以自定义一个类,它的宽度与高度等于scale乘以对应的宽高。

// 缩放的Mixin
template <class Base, int ScaleX, int ScaleY>
class Scale : public Base {
 public:
  int GetScaledWidth() const { return Base::GetWidth() * ScaleX; }
  int GetScaledHeight() const { return Base::GetHeight() * ScaleY; }
};

像这种场景非常适合使用mixin。

使用的时候:

using ModifiedShape = Border<RotateBy90<Scale<Square, 2, 1>>, 10>;

示例2:redo与undo

完整示例模拟一个redo与undo操作,我们便可以在数据之间来回的切换。Redoable<Undoable<Number>> ,在这里我们可以将这些类混合到一起使用了。

#include <iostream>

using namespace std;

struct Number {
    typedef int value_type;
    int n;
    void set(int v) {
        n = v;
    }
    int get() const {
        return n;
    }
};

template <typename BASE, typename T = typename BASE::value_type>
struct Undoable : public BASE {
    typedef T value_type;
    T before;
    void set(T v) {
        before = BASE::get();
        BASE::set(v);
    }
    void undo() {
        BASE::set(before);
    }
};

template <typename BASE, typename T = typename BASE::value_type>
struct Redoable : public BASE {
    typedef T value_type;
    T after;
    void set(T v) {
        after = v;
        BASE::set(v);
    }
    void redo() {
        BASE::set(after);
    }
};

typedef Redoable<Undoable<Number>> ReUndoableNumber;

int main() {
    ReUndoableNumber mynum;
    mynum.set(42);
    mynum.set(84);
    cout << mynum.get() << '\n';  // Output: 84
    mynum.undo();
    cout << mynum.get() << '\n';  // Output: 42
    mynum.redo();
    cout << mynum.get() << '\n';  // Output: back to 84
}

示例3:重复打印

再举一个拼积木的例子:重复打印。

首先,有一个类,我们可以print这个人的名字。

class Name
{
public:
    Name(std::string firstName, std::string lastName)
      : firstName_(std::move(firstName))
      , lastName_(std::move(lastName)) {}
    
    void print() const
    {
        std::cout << lastName_ << ", " << firstName_ << '\n';
    }
    
private:
    std::string firstName_;
    std::string lastName_;
};

那如果我想重复打印呢?

我们可以这样玩,使用mixin,将Name类传递进去。

template<typename Printable>
struct RepeatPrint : Printable
{
    explicit RepeatPrint(Printable const& printable) : Printable(printable) {}
    void repeat(unsigned int n) const
    {
        while (n-- > 0)
        {
            this->print();
        }
    }
};

template<typename Printable>
RepeatPrint<Printable> repeatPrint(Printable const& printable)
{
    return RepeatPrint<Printable>(printable);
}

使用:

Name ned("Eddard", "Stark");    
repeatPrint(ned).repeat(10);

此时,便得到重复n次的打印结果。

那么mixin比较适合什么场景呢?

  • 保持原来的类不变,

  • 客户端代码不直接使用原始类,它需要将其包装到 mixin 中才能使用增强功能,

标准库

在标准库当中有一个使用mixin技术:std::nested_exception。

std::nested_exception 是一个多态 mixin 类,它可以捕获并存储当前异常,从而可以在彼此之间嵌套任意类型的异常。

template <typename _Except>
struct _Nested_exception : public _Except, public nested_exception
{}

在源码中我们可以看到使用了nested_exception作为mixin的基类。

参考

https://cedanet.com.au/ceda/Xc++/$mixin/mixins-in-straight-c++.php

https://www.fluentcpp.com/2017/12/12/mixin-classes-yang-crtp/

https://stackoverflow.com/questions/18773367/what-are-mixins-as-a-concept

1b9fba836d64538003611dd188b79195.png

048a1622ca58394d897008acc50ca915.jpeg

相关推荐

  1. C++assert惯用

    2024-05-16 13:04:09       30 阅读
  2. C++ 中的 Pimpl 惯用

    2024-05-16 13:04:09       36 阅读
  3. C++ Pimpl惯用(桥接模式特例)智能指针

    2024-05-16 13:04:09       41 阅读
  4. C++惯用: 通过std::decltype来SFINAE掉表达式

    2024-05-16 13:04:09       23 阅读
  5. C++惯用:do...while(0)的妙用

    2024-05-16 13:04:09       26 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-16 13:04:09       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-16 13:04:09       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-16 13:04:09       82 阅读
  4. Python语言-面向对象

    2024-05-16 13:04:09       91 阅读

热门阅读

  1. iOS 数据库升级

    2024-05-16 13:04:09       29 阅读
  2. LeetCode 22. 括号生成

    2024-05-16 13:04:09       53 阅读
  3. 循环、使用dict和set

    2024-05-16 13:04:09       34 阅读
  4. 使用 Gradle 自定义任务生成初始化 SQL 文件

    2024-05-16 13:04:09       37 阅读
  5. 数学建模(科普)

    2024-05-16 13:04:09       38 阅读
  6. IT行业的现状与未来:技术驱动下的新世界

    2024-05-16 13:04:09       39 阅读
  7. js 数组filter使用

    2024-05-16 13:04:09       33 阅读
  8. RIP、OSPF、BGP等协议及华为路由器配置总结

    2024-05-16 13:04:09       37 阅读
  9. 40-2 了解与安装堡垒机

    2024-05-16 13:04:09       33 阅读
  10. Leetcode 404:左叶子之和

    2024-05-16 13:04:09       35 阅读
  11. 力扣:131. 分割回文串

    2024-05-16 13:04:09       31 阅读