如何在二维类型层次结构中提供类型安全性?

时间:2016-10-02 22:27:16

标签: c++ inheritance type-conversion c++17

考虑以下类层次结构:

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 *。

我可以看到三种解决方案,都有不幸的缺点:

  1. 覆盖类[B | C | D] V中的std :: vector成员以转换为正确的指针类型
    • 明显的缺点:很多样板代码
  2. 让AV不从std :: vector继承而是使[B | C | D] V继承自相应的std :: vector&lt; [A | B | C] *&gt;实例
    • 明显的缺点:AV不能再用于只使用A类元素成员的代码。
  3. 不要将[A | B | C | D] V定义为类层次结构,而是将其作为类模板,提供每个模板参数的实现[A | B | C | D]
    • 明显的缺点:隐含层次信息的丢失,尤其是使用IDE工具等IDE工具 - &gt;代码变得越来越难以导航
  4. 请注意,如果容器是成员而不是类[A | B | C | D] V的父类,则问题仍然存在。

    对窘境有更好的解决方案吗?如果它是惯用的,它是如何调用的?

    为了清晰起见,简化:

    • C指针而不是更合适的std :: weak_pointer
    • 简短的班级名称,而不是告诉他们
    • 继承STL容器,免费提供容器函数[s | ality],继承的目的也是如此。

    将STL容器作为成员提供,将导致在这些行中实现其大部分功能的委托:

    class AV
    {
    public:
      auto begin() { return this->container.begin(); }
      auto end()   { return this->container.end(); }
      /* etc. pp. */
    protected:
      std::vector<A*> container;
    }
    

2 个答案:

答案 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个插脚。阅读,写作和实施。

AVAV_reader(等)的基类。 BV_reader上的非变异操作满足BV的原因和后置条件,假设AVA也是如此。

对于写作,情况并非如此; B不是BV_writer的一种。可能它可能是颠倒的:AV_writer是一种AV_writer,但它又依赖于BV_writerA属性,这些属性有点不太常见比阅读不变量。此外,一些扩展程序(B)赢了;是BV_writer_augment的一部分。参数的逆变可以免费获得这一点。

在实现方面,使用AV_writer来存储vector<A*>并将其包含在无数的演员阵容中,并且很容易出错。

结论是:

你想要协变阅读,逆变写作,(我认为我有两个正确的名称:我有时会反过来)和一个模板类似CRTP的实现系统,它进行类型检查,存储实际的底层向量而不强制不进行强制转换。

这与C ++默认的多态继承指针系统不匹配。所以不要使用C ++指针多态继承。

现在的问题是,正确的解决方案很难,并需要一些样板。但这是正确的做法。