考虑以下类层次结构:
class A {};
class B : public A {};
class C : public A {};
class D : public A {};
假设它不是简单的可简化的:B,C和D至少有一个成对的析取成员定义,并且至少有一个共享成员定义从A继承。
现在考虑容器的层次结构,其中包含指向第一个对象的指针:
class AV : public std::vector<A*> {};
class BV : public AV {}; // contains only B*
class CV : public AV {}; // contains only C*
class DV : public AV {}; // contains only D*
同样,假设它不能如上所述简单地减少。
这些类应该是“具有特征的容器”:为“元素和函数”提供常用的容器函数,这些函数的行为是“获得平均值”,“是否满足任何元素”等。 V上的操作总是假设{A,B,C,D}中的x类的元素。
现在问题是AV的子节点提供了在A *上实例化的std :: vector函数,而不是它们的实际内容B *,C *,D *。
我可以看到三种解决方案,都有不幸的缺点:
请注意,如果容器是成员而不是类[A | B | C | D] V的父类,则问题仍然存在。
对窘境有更好的解决方案吗?如果它是惯用的,它是如何调用的?
为了清晰起见,简化:
将STL容器作为成员提供,将导致在这些行中实现其大部分功能的委托:
class AV
{
public:
auto begin() { return this->container.begin(); }
auto end() { return this->container.end(); }
/* etc. pp. */
protected:
std::vector<A*> container;
}
答案 0 :(得分:0)
应该这样做。
template<typename L>
class V : public std::vector<L*> {}; // all members of original AV
class AV : public V<A> {}; // now empty
class BV : public V<B> {}; // as before
class CV : public V<C> {}; // " "
class DV : public V<D> {}; // " "
如果我没有记错的话,不应该对原始设置施加进一步的限制。
此时我们离@SamVarshavchik的原始评论只有一步之遥,这是这个问题上的第一个活动。有时最简单的答案是最好的 - 也是最难发现的。
答案 1 :(得分:0)
""
不是BV
。那里不应该有继承,因为AV
没有有用的方法来满足一个相当有用的可变BV
的不变量。在这里拥有(C ++)继承是有害的。
这是旧的矩形正方形问题,其中不可变的正方形是不可变的矩形,但是可变的正方形不是可变的矩形。
有3个插脚。阅读,写作和实施。
AV
是AV_reader
(等)的基类。 BV_reader
上的非变异操作满足BV
的原因和后置条件,假设AV
和A
也是如此。
对于写作,情况并非如此; B
不是BV_writer
的一种。可能它可能是颠倒的:AV_writer
是一种AV_writer
,但它又依赖于BV_writer
和A
属性,这些属性有点不太常见比阅读不变量。此外,一些扩展程序(B
)赢了;是BV_writer_augment
的一部分。参数的逆变可以免费获得这一点。
在实现方面,使用AV_writer
来存储vector<A*>
并将其包含在无数的演员阵容中,并且很容易出错。
结论是:
你想要协变阅读,逆变写作,(我认为我有两个正确的名称:我有时会反过来)和一个模板类似CRTP的实现系统,它进行类型检查,存储实际的底层向量而不强制不进行强制转换。
这与C ++默认的多态继承指针系统不匹配。所以不要使用C ++指针多态继承。
现在的问题是,正确的解决方案很难,并需要一些样板。但这是正确的做法。