C++中的对象组合

Object composition in C++

本文关键字:组合 对象 C++      更新时间:2023-10-16

我正在尝试在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++而言,这并不是最重要的,正如你清楚地看到的那样——而且有充分的理由。您的建议有许多缺点,因此是不允许的。但是,有几种方法可以解决此限制。

一种方法是使用虚拟继承,并定义一个抽象基类(称为,例如Store(,它通过Stack中实现的虚拟功能提供对PusherPopper运行的存储的访问。

但是,这种方法也存在许多问题,通常在C++中避免使用。一种更惯用的方法使用奇怪的重复模板模式(CRTP(。

将您的PusherPopper更改为将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适配器类的接口以获取灵感。