我正在尝试在C ++中实现某种对象组合。这个想法是创建所有数据结构的集合,可以通过组成仅知道基本数据结构操作的抽象类来创建所有数据结构。例如,通过将推子和弹出器组成来创建堆栈,通过将队列器和弹出器组成来创建队列,等等。
问题是,即使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也知道,这就是重要的。但是,此代码无效,无法编译。如何写出具有相同效果的有效内容?
即使Pusher和Popper不知道元素,Stack也知道,这很重要。
没有正如您可以清楚地看到的那样,对于C ++而言,这是not重要的所有部分,并且有充分的理由。您的建议有很多缺点,因此是不允许的。但是,有几种方法可以解决此限制。
一种方法是使用虚拟继承,并定义抽象基类(例如,称为Store
),该抽象基类提供对通过Pusher
和Popper
进行操作的虚拟函数的存储的访问, Stack
。
但是,this approach also has numerous problems并且通常在C ++中避免。更惯用的方法是使用the Curiously recurring template pattern (CRTP)。
将Pusher
和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
container adapter接近,是将std::stack
和Pusher
实现为Popper
:也就是说,它们继承自decorators,而不是相反。这可能很有用,但仅当您更改名称时:显然,具有既不执行推入也不弹出的类Stack
是没有意义的。再次,查看Stack
适配器类的接口以获取灵感。