2016-10-02 31 views
0

考虑下面的类层次结构:如何在二维类型层次结构中提供类型安全性?

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的儿童提供的,而不是B *自己的实际内容实例化A *的std ::向量函数,C *,d *分别。

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

  1. 倍率的std ::向量成员[B | C | d] V至转换为正确的指针类型
    • 明显的缺点:大量的样板代码
  2. 设AV不从标准::矢量继承和代替使[B | C | d】V从相应的std ::矢量继承< [A | B | C] *>实例化
    • 明显的缺点:AV不再能够用于其中来自A类只元件构件中使用的代码。
  3. 没有定义[A | B | C | d] V作为类层次结构,但作为类模板,提供每个模板参数实现[A | B | C | d]
    • 明显的缺点:损失隐含层次结构信息,特别是使用IDE工具如Visual Studio - >代码越来越难以导航

注意,如果容器是构件代替的父类的问题仍然存在[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; 
} 
+1

4.实现一个完全类型安全的模板。这会导致代码臃肿,但这就是C++的全部内容。 –

+0

不要从std :: vector继承..避免存储指针(甚至是智能指针),除非你需要多态。 –

+0

我认为你应该**重构**避免从'std :: vector'继承的问题。这对读者来说是一条红鲱鱼。正如你注意到的那样,它不是你的问题的必要条件 –

回答

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的原始评论只有一步之遥,这是偶然发生在这个问题上的第一个活动。有时最简单的答案是最好的 - 也是最难以发现的。

+0

注意:我知道我可以通过用另一个模板类V替换AV并将AV推广到与[B | C | D] V相同的图层上的非模板来改善此问题。编辑这个问题来反映这个问题将意味着摆脱对这个默认模板论点的混淆,但是我想看看是否有人能够为我提供启发;在一段时间内,我会提供相应的编辑,使这个答案可以接受。 – Zsar

+1

我不确定这是否是对问题的回答。我觉得这些信息会更好地适合这个问题,作为第四个解决方案,带来不幸的缺点。 – Tas

+0

@Zsar:“*根据cppreference.com,这不应该是这种情况*”AV'和'AV <>'有区别。前者是一个模板;后者是一类。 –

0

A BV不是AV。在那里不应该存在继承,因为BV没有用来满足合理有用的可变的不变式AV。在这里继承(C++)是有害的。

这是旧的矩形正方形问题,其中一个不可变的正方形是一个不可变的矩形,但是一个可变的正方形不是一个可变的矩形。

有3个叉。阅读,写作和实施。

AV_readerBV_reader(etc)的基类。 BV上的非变异操作满足AV的原因和后置条件,假设AB也是如此。

对于写作而言,情况并非如此; BV_writer不是一种AV_writer。可能它可能是颠倒的:AV_writer是一种BV_writer,但是这又依赖于AB属性,并且这些属性与读取不变量相比不太常见。还有一些分机(BV_writer_augment)赢得; t是AV_writer的一部分。参数的变化可能会让你免费。

在实现方面,使用vector<A*>来存储vector<B*>并将其包装在无数的casts中,这很容易出错并且很痛苦。

得出的结论是:

你想协变读,逆变写作,(我觉得我有这两个名称权:我有时反转他们)和模板CRTP样implementarion系统,不能进行类型检查存储实际底层载体而不强制不施放。

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

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

+0

Mmh。 Mayhap我没有详细说明。 Mayhap命名帮助。假设AV是“UnitList”,BV是“LandUnitList”; AV的所有功能必须在BV中可用,有些功能是虚拟的并且被覆盖,但大多数只是继承了功能。在这种情况下,A将是“单位”,B将是“LandUnit”。 LandUnit将具有附加值,但是单元的所有值都是Unit的所有功能。 Unit-> LandUnit和UnitList-> LandUnitList都是有效的继承,只要后者总是(并且被用作)完全功能的前者。 – Zsar

+0

@zsar违反[LSP](https://en.wikipedia.org/wiki/Liskov_substitution_principle),如果您可以写入元素或将元素添加到'AV'。将“LandUnit”添加到实际为“WaterUnitList”的“UnitList”是这种违规的一个症状。一个LandUnit可以是一个Unit,但是一个LandUnitList不是一个UnitList *,如果有类似写操作的话*,因为LandUnitList的写入预处理(源代码是一个LandUnit)比UnitList写前提条件(源代码是一个Unit)更严格。 ReadOnlyLandUnitList是一个ReadOnlyUnitList。 – Yakk

+0

有趣的是,WriteOnlyUnitList是一个WriteOnlyLandUnitList,因为WriteOnlyUnitList上的前提条件比WriteOnlyLandUnitList更弱。如果你不熟悉这个技巧,你会想坐下来重新考虑你的虚拟“什么是地狱,不是”。 (简而言之,编写“继承”是从阅读*向后*)。 – Yakk

相关问题