调用不在基类中的派生类函数而不进行动态强制转换,以最大程度地提高性能

Call a derived class function not in base class without dynamic casting to maximize performance

本文关键字:转换 高性能 程度地 基类 派生 类函数 调用 动态      更新时间:2023-10-16

我正在C++中实施AVLTree,作为为未来项目做准备的练习。AVLTree 基本上是一个 BSTTree,但树是平衡的,即对于具有左子级 y 和右子级 z 的节点 x,x 左侧的子节点数(子级 y 和 y 的子级(与 x 右侧的子节点数相差 1 个以上。

每个节点将表示一个 int 值,并具有对父节点、可能存在的左子节点和可能存在的右子节点的引用。

class BSTNode {
protected:
int value;
BSTNode* parent;
BSTNode* left;
BSTNode* right;
...
public:
virtual void setLeftChild(BSTNode* left); 
...
}

为了跟踪节点的子节点数量,我让AVLNode用两个整数扩展BSTNode,leftAffinity和rightAffinity,其中leftAffinity告诉我左侧的节点数量(与rightAffinity的右侧类似(。由于我不确保要添加的值始终是唯一的,因此在找到放置新节点的位置之前,我无法更新相关性。

class AVLNode : public BSTNode {
private:
int leftAffinity;
int rightAffinity;
...
public:
void setLeftChild(BSTNode* left) override;
...
}

一旦我成功地将节点的左子节点设置为节点,在 AVLNode 中,我还会将当前节点的leftAffinity更新(并递归到父节点的父节点,直到到达树的根(到left->getLeftAffinity() + left->getRightAffinity().

这里的问题是亲和力函数是在 AVLNode 中定义的,因此我不能在没有强制转换的情况下立即调用 left->getLeftAffinity((,因为在这里我不知道left是 BSTNode 还是 AVLNode。我知道这个想法是让AVLNode的任何子级也只能是AVLNode,这可以通过确保任何不是AVLNode的BSTNode转换为AVLNode来强制执行。

  • 我不想更改函数参数来接收 AVLNode*,因为这迫使我在 AVLNode 类中将左、右和父声明为 AVLNode*,因此我得到了重复的变量,BSTNode 类各一个,AVLNode 类各一个,即使左、右和父在 BSTNode 类中是私有的。
  • 我不想使用动态强制转换,因为练习的目的是创建一个高效的树数据结构,O(log n)插入和获取操作的复杂性。我读过动态铸造既昂贵又应该避免,因为它通常暗示设计缺陷。

可能的方法:

  • 在 BSTNode 类中创建一个"no-op"函数,该函数在 AVLNode 类中被覆盖以管理节点亲和性。这个函数不应该存在,因为它与BSTNode无关。
  • 对每个添加操作进行动态强制转换,并在成功时为每个父级执行一次,直到树的根,这太昂贵了。
  • 根本不为这些功能使用虚拟,这将迫使我在 AVLNode 中重载许多函数。
  • 更改所有函数以接收 AVLNode* 而不是 BSTNode*,同时使用 AVLNode 中的构造函数强制将 BSTNode* 转换为 AVLNode*,但这需要动态强制转换,否则我将失去正在添加的节点的 leftAffinity 和 rightAffinity,并导致函数继承的其他问题。

对我来说,最好的选择是采用"无操作"方法,因为如果性能是关键,这种方法会起作用。

我是C++新手,所以我感谢对我所写的方法的任何想法,例如,是否有我忽略的微不足道的解决方案。在这种情况下,最好的选择是什么?

当您使用基类只是为了在不同实现之间共享代码而不是约束方法参数和返回值时,会出现这样的问题。

BSTNode可能没有在这个角色中增加足够的价值来证明它产生的并发症是合理的,你可能应该摆脱它。

但有时这样的事情对于为难以实现的自引用类提供框架基础实现很有用。 在C++中,类型正确的方法是使用奇怪命名的奇怪重复出现的模板模式:https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

使用此模式,基类定义为将派生类作为参数的模板:

template <class NODE> class BSTNode {
int value;
NODE *left;
NODE *right;
...
}
class AvlNode : public BSTNode<AvlNode> {
...
}

现在,AvlNode中的所有指针都具有正确的类型,并且不需要强制转换。