如何构造从泛型树到 a-b 树的继承

Posted

技术标签:

【中文标题】如何构造从泛型树到 a-b 树的继承【英文标题】:How to structure the inheritance from generic tree to a-b tree 【发布时间】:2020-07-28 09:31:08 【问题描述】:

我正在尝试实现 a-b 树,作为通用树的派生类。 通用树节点如下:

template<typename T>
struct TreeNode

    T value;
    std::vector<TreeNode*> children;
    //Some other trivial stuff
;

a-b节点的结构如下:

template<typename T>
struct ABTreeNode : TreeNode<T>

    std::vector<T> keys;
    //The idea is to omit the T value field of the base node and use that vector for the keys
;

在通用树类中也存在一个根字段

TreeNode *root;

而 a-b 构造函数是

template<Typename T>
ABTree<T>::ABTree(T value)

    GenericTree<T>::root = new ABTreeNode;
    root->keys.push_back(value);

现在,这种方式,我需要在很多 a-b 树方法中使用向下转换,例如:

template<typename T>
bool ABTree<T>::search(T value)

    ABTreeNode *node = GenericTree<T>::root;
    //....
//Downcast base to derived

据我所知,向下转换是一种不好的做法,表明设计不好。我使用派生结构中定义的变量但将节点声明为基本结构这一事实似乎很容易出错。如果该节点被创建为基本节点而不是派生的,会发生什么? 例如:

//Somewhere:
TreeNode *node = new TreeNode;//Instead of new ABTreeNode
//..
//Somewhere else
node->keys//Shouldn't that be an error?

我的方法正确吗?如果不是,我应该如何更好地构建它? PS:请保留原始指针。

【问题讨论】:

如果要删除基类的某些数据成员,请不要从该基类继承。 那么在这种情况下没有办法使用继承吗?或者在最坏的情况下我应该将键向量放在基本结构中吗? 为什么需要继承?如果没有继承,ABTreeNode 应该如何?顺便说一句,如果您知道自己在做什么,那么沮丧并没有那么糟糕。例如,std::list 的实现充满了它们。 【参考方案1】:

通过继承共享代码是一种糟糕的设计。更好的是使用 Composition - 请参阅 https://en.wikipedia.org/wiki/Composition_over_inheritance

为了在各种树的不同实现之间共享代码,我会将公共字段提取到一个结构中。

template <class T, class ChildT>
struct TreeNodeCommons

  T nodeValue;
  std::vector<ChildT*> children;
  // more common fields

然后我会将它附加到不同类型的节点上。

template<typename T>
struct ABTreeNode

  TreeNodeCommons<T, ABTreeNode<T>> commons;
  std::vector<T> keys;  
;

然后您可以编写模板化算法,假设 Node 包含名为 commons 的字段,并且您也可以编写特定于 Node 的算法。而且没有dynamic_casts

【讨论】:

非常感谢,这可能就是我要做的。不过有一件事,如果通过继承共享代码不好,那么我应该什么时候使用它? 如果您有相同接口的两种实现(主要是 - 一种用于测试,一种用于产品)。如果你想有 pimpl 成语。从实际的角度来看,我使用继承的几乎所有情况都是:没有成员的纯虚拟基类(只有 = 0 的方法)和实现类。 我不明白为什么它是糟糕的设计。这就是typical std::list node 的定义方式:struct Node_base Node_base* next; ; template&lt;class T&gt; struct Node : Node_base T data; ;。这是糟糕的设计吗? @Evg 好点。查看 std::list 我认为它们将 Node 和 Node_base 分开,因此 Node_base 不包含任何模板化属性。这在实例化期间节省了类大小。我认为这是性能比拥有最美观的设计更重要的情况。

以上是关于如何构造从泛型树到 a-b 树的继承的主要内容,如果未能解决你的问题,请参考以下文章

Scala:从泛型类型到第二泛型类型的隐式转换

如何从泛型类访问静态变量?

如何从泛型函数返回 null? [复制]

关于泛型

如何获取和使用从泛型推断的类型

如何从泛型函数中记录参数[重复]