C++ 什么是混合(作为一个概念)

C++ 什么是混合(作为一个概念),c++,oop,templates,mixins,C++,Oop,Templates,Mixins,我试图让我的头周围的混合概念,但我似乎不明白它是什么。 在我看来,这是一种通过使用继承来扩展类功能的方法。 我读到人们把它们称为“抽象子类”。有人能解释为什么吗 如果您能根据以下示例(来自我的一个讲座幻灯片)解释您的答案,我将不胜感激: mixin是一个类,通常通过提供功能所需的基本特性的指定类为另一个类提供功能。例如,考虑你的例子: 本例中的mixin提供撤消值类的set操作的功能。此能力基于参数化类(示例中的Number类)提供的get/set功能 另一个示例(摘自): 模板 类计数:公共图

我试图让我的头周围的混合概念,但我似乎不明白它是什么。 在我看来,这是一种通过使用继承来扩展类功能的方法。 我读到人们把它们称为“抽象子类”。有人能解释为什么吗

如果您能根据以下示例(来自我的一个讲座幻灯片)解释您的答案,我将不胜感激:
mixin是一个类,通常通过提供功能所需的基本特性的指定类为另一个类提供功能。例如,考虑你的例子:
本例中的mixin提供撤消值类的set操作的功能。此能力基于参数化类(示例中的
Number
类)提供的
get/set
功能

另一个示例(摘自):

模板
类计数:公共图{
已访问的int节点,已访问的边;
公众:
计数():已访问的节点(0)、已访问的边(0)、图(){}
节点成功节点(节点v){
节点_++;
返回图::成功节点(v);
}
边成功边(边e){
边(u++);
返回图::成功边(e);
}
... 
};
在本例中,mixin提供了计算顶点的功能,给定了一个执行trasversal操作的graph类

通常,C++混合语言是通过习语来实现的。此线程可以很好地阅读C++中的mixin实现:

下面是一个利用CRTP习惯用法的mixin示例(感谢@Simple):

#包括
#ifndef NDEBUG
#包括
#恩迪夫
阶级形态
{
公众:
形状*克隆()常量
{
形状*const p=do_clone();
断言(p&“do_clone不能返回空指针”);
断言(
typeid(*p)=typeid(*this)
&&“do_clone必须返回指向相同类型对象的指针”
);
返回p;
}
私人:
虚拟形状*do_clone()常量=0;
};
模板
类可克隆形状:公共形状
{
私人:
虚拟形状*do_clone()常量
{
返回新的D(静态_cast(*this));
}
};
类三角形:公共可克隆_形
{
};
类广场:公共可克隆_形
{
};

此mixin提供了一组形状类(层次结构)的异构复制功能。

这与接口的工作原理相同,可能更像是一个抽象,但接口更容易第一次获得

它解决了许多问题,但我发现在开发中经常出现的一个问题是外部API。想象一下

你有一个用户数据库,这个数据库有一种访问数据的方式。 现在假设你有facebook,它也有一种访问其数据(api)的特定方式

在任何时候,您的应用程序都可能需要使用facebook或数据库中的数据来运行。所以你要做的就是创建一个接口,上面写着“任何实现我的东西都肯定会有以下方法”,现在你可以在你的应用程序中实现这个接口了

因为接口承诺实现存储库将在其中声明方法,所以您知道,无论何时何地在应用程序中使用该接口,如果您切换数据,它总是会有您正在定义的方法,因此有数据要处理

这种工作模式有更多的层,但本质是它是好的,因为数据或其他此类持久性项成为应用程序的一大部分,如果它们在您不知道的情况下发生更改,您的应用程序可能会崩溃:)

这里有一些伪代码

interface IUserRepository
{
    User GetUser();
}

class DatabaseUserRepository : IUserRepository
{
    public User GetUser()
    {
        // Implement code for database
    }
}

class FacebookUserRepository : IUserRepository
{
    public User GetUser()
    {
        // Implement code for facebook
    }
}

class MyApplication
{
    private User user;

    MyApplication( IUserRepository repo )
    {
        user = repo;
    }
}

// your application can now trust that user declared in private scope to your application, will have access to a GetUser method, because if it isn't the interface will flag an error.

在讨论混合是什么之前,描述它试图解决的问题是很有用的。假设你有一堆想法或概念,你正试图建模。它们可能在某种程度上是相关的,但它们在很大程度上是正交的——这意味着它们可以彼此独立地站在一起。现在,您可以通过继承对此进行建模,并使这些概念中的每一个都派生自某个公共接口类。然后在实现该接口的派生类中提供具体方法

这种方法的问题在于,这种设计没有提供任何清晰直观的方法来获取每个具体类并将它们组合在一起

mix-ins的想法是提供一组基本类,其中每个基本类都建模一个基本的正交概念,并且能够将它们粘在一起,组成具有您想要的功能的更复杂的类——有点像legos。原语类本身被用作构建块。这是可扩展的,因为以后可以向集合中添加其他基本类,而不会影响现有的基本类

回到C++,使用模板和继承的方法。这里的基本思想是通过模板参数将这些构建块连接在一起。然后将它们链接在一起,例如通过

typedef
,形成包含所需功能的新类型

以您的示例为例,假设我们希望在顶部添加重做功能。下面是它的样子:

#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';  // 84
  mynum.undo();
  cout << mynum.get() << '\n';  // 42
  mynum.redo();
  cout << mynum.get() << '\n';  // back to 84
}
#包括
使用名称空间std;
结构编号
{
typedef int value_type;
int n;
空集(intv){n=v;}
int get()常量{return n;}
};
模板
结构可撤消:公共基
{
类型定义T值_类型;
T前;
void set(tv){before=BASE::get();BASE::set(v);}
void undo(){BASE::set(before);}
};
模板
struct Redoable:公共基
{
类型定义T值_类型;
T后;
空集(tv){after=v;BASE::set(v);}
void redo(){BASE::set(after);}
};
typedef RedoableReUndoableNumber;
int main()
{
可重复数mynum;
mynum.set(42);mynum.set(84);

cout我喜欢greatwolf的答案,但要注意一点。
interface IUserRepository
{
    User GetUser();
}

class DatabaseUserRepository : IUserRepository
{
    public User GetUser()
    {
        // Implement code for database
    }
}

class FacebookUserRepository : IUserRepository
{
    public User GetUser()
    {
        // Implement code for facebook
    }
}

class MyApplication
{
    private User user;

    MyApplication( IUserRepository repo )
    {
        user = repo;
    }
}

// your application can now trust that user declared in private scope to your application, will have access to a GetUser method, because if it isn't the interface will flag an error.
#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';  // 84
  mynum.undo();
  cout << mynum.get() << '\n';  // 42
  mynum.redo();
  cout << mynum.get() << '\n';  // back to 84
}
int main()
{
  ReUndoableNumber mynum;
  Undoable<Number>* myUndoableNumPtr = &mynum;

  mynum.set(42);                // Uses ReUndoableNumber::set
  myUndoableNumPtr->set(84);    // Uses Undoable<Number>::set (ReUndoableNumber::after not set!)
  cout << mynum.get() << '\n';  // 84
  mynum.undo();
  cout << mynum.get() << '\n';  // 42
  mynum.redo();
  cout << mynum.get() << '\n';  // OOPS! Still 42!
}