【发布时间】:2017-02-10 20:28:15
【问题描述】:
考虑以下类层次结构:
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*。
我可以看到三种解决方案,都有不幸的缺点:
- 覆盖类 [B|C|D]V 中的 std::vector 成员以强制转换为正确的指针类型
- 明显的缺点:大量样板代码
- 让 AV 不从 std::vector 继承,而是让 [B|C|D]V 从相应的 std::vector 实例化继承
- 明显的缺点:AV 不能再用于只使用类 A 的元素成员的代码。
- 不要将 [A|B|C|D]V 定义为类层次结构,而是作为类模板,为每个模板参数 [A|B|C|D] 提供实现
- 明显的缺点:隐式层次信息丢失,尤其是。使用 Visual Studio 等 IDE 工具 -> 代码变得更难导航
请注意,如果容器是类 [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;
}
【问题讨论】:
-
4.实现一个完全类型安全的模板。这将导致代码膨胀,但这就是 C++ 的全部意义所在。
-
不要从 std::vector 继承。避免存储指针(甚至是智能指针),除非你需要多态性。
-
我认为您应该重新表述避免从
std::vector继承的问题。这对读者来说是一个红鲱鱼。正如您所指出的,这对您的问题并不重要 -
您隐含地和明确地提出了多个复杂的问题。最麻烦的是这种并行层次结构很容易破坏类型安全。考虑
struct A{}; struct B:A{}; struct PA{ A*& foo(); }; struct PB: PA { B*& foo(); };,其中foo和PA中的PB访问相同的原始指针成员。然后可以将PB实例绑定到PA&,并且可以设置指针指向C,打破PB的类不变量。 -
另一个复杂的问题与施工安全有关,避免两阶段施工(或更糟)。这是 C++ GUI 框架中解决的一个众所周知的问题。这样的框架通常为每个较低级别的 C API 小部件提供一个高级 C++ 小部件,即与您的问题中的并行层次结构大致相同。您添加的主要复杂性是每个高级对象的一个 wrappee 向量,而不是单个 wrappee。看起来还是一样的问题。
标签: c++ inheritance type-conversion c++17