0

我正在尝试在 C++ 中实现某种对象组合。这个想法是创建一组所有数据结构,这些数据结构可以通过组合只知道基本数据结构操作的抽象类来创建。例如,可以通过将 pusher 与 popper 组合来创建堆栈,通过将 queuer 与 popper 组合来创建队列等。

问题是,尽管 Pusher、Popper 和 Queuer 只是作为抽象类,并且它们永远不会被实例化,它们必须知道数据结构内部是如何存储数据的。

目标是拥有不可知的抽象类,这些抽象类仅用于将方法定义传递给具体的数据结构类,方式如下:

class Pusher {
  public:
    void Push(int value) {
      // Simulates internal logic of pushing a value.
      elements.push_back(value);
    }
}

class Popper {
  public:
    int Pop() {
      // Simulates internal logic of popping a value.
      int popped_value = elements.back();
      elements.pop_back();
      return popped_value;
    }
}

class Stack: public Pusher, public Popper {
  private:
    vector<int> elements;
}

你可以看到,尽管 Pusher 和 Popper 不知道元素,但 Stack 知道,这才是最重要的。但是,此代码无效且无法编译。我怎样才能写出有效的东西来达到同样的效果?

4

1 回答 1

1

尽管 Pusher 和 Popper 不知道元素,但 Stack 知道,这才是最重要的。

不。正如您可以清楚地看到的那样,就 C++ 而言,这并不是最重要的——而且有充分的理由。你的建议有很多缺点,因此是不允许的。但是,有几种方法可以解决此限制。

一种方法是使用虚拟继承,并定义一个抽象基类(例如,称为Store),该基类提供对存储的访问,PusherPopper通过在 中实现的虚拟函数对其进行操作Stack

然而,这种方法也有很多问题,通常在 C++ 中避免使用。更惯用的方法是使用Curiously recurring template pattern (CRTP)

Pusher将您的and更改Popper为将Stack类作为模板参数的类模板:

template <typename T>
class Pusher {
  public:
    void Push(int value) {
      // Simulates internal logic of pushing a value.
      T::elements(*this).push_back(value);
    }
};

template <typename T>
class Popper {
  public:
    int Pop() {
      // Simulates internal logic of popping a value.
      int popped_value = T::elements(*this).back();
      T::elements(*this).pop_back();
      return popped_value;
    }
};

class Stack: public Pusher<Stack>, public Popper<Stack> {
  public:
    template <typename T>
    static std::vector<int>& elements(T& s) {
        return static_cast<Stack&>(s).elements_;
    }
  private:
    std::vector<int> elements_;
};

不用说这仍然很复杂,因为您的数据依赖性是倒置的。仔细考虑你的特征需要哪些依赖关系,以及它们如何有用。

另一种实现,接近标准库的std::stack容器适配器的实现,是实现PusherPopper作为装饰器:也就是说,它们继承自Stack而不是相反。这可能很有用,但前提是您更改名称:显然拥有一个Stack既不执行推送也不执行弹出的类是没有意义的。再次,查看std::stack适配器类的接口以获得灵感。

于 2020-01-29T18:12:24.843 回答