从派生类重新定义基类中定义的结构

Posted

技术标签:

【中文标题】从派生类重新定义基类中定义的结构【英文标题】:Redefining a structure defined in a base class from a derived class 【发布时间】:2017-05-19 06:10:26 【问题描述】:

我在 C++ 中为二叉搜索树创建了一个类(名为 BST),现在尝试为继承 BST 类的 AVL 树创建一个类(名为 AVL)。我在我的 BST 类中定义了一个结构(命名节点),现在想在派生的 AVL 类中使用它时为其添加一个额外的成员“高度”。我找不到如何重新定义结构并仅在用于 AVL 类时为其添加额外成员的方法。 这是我的 BST 类代码:

class BST

protected:
struct node

    int val;
    node* left;
    node* right;
;
node* root;

public:
BST()

    this->root = NULL;

void insert(int val);
void display();
void displayPretty();
void displaySorted();
int min();
int max();
int height();
void remove(int val);
int after(int val);
int before(int val);
static bool isBST(BST tree);
~BST()

    removeAll(this->root);


protected:
node* create(int val);
node* insertInto(node* root,int val);
void displayLevelOrder(queue<node*>* q);
void displayPretty(node* p, int indent);
void displayInorder(node* root);
node* getMin(node* root);
node* getMax(node* root);
int heightOf(node* root);
node* removeFrom(node* root,int val);
void removeAll(node* root);
node* getSuccessorOf(int val,node* root,node* prev);
node* getPredecessorOf(int val,node* root,node* next);
static bool isBST(node* root,int min,int max);

;

我不完整的 AVL 类代码是:

class AVL : public BST

protected:
struct node

    // redefine node here (by adding the 'height' member)
    // such that all the node variables/pointers in BST (e.g. root)
    // also get created with this new definition
;

public:
AVL() : BST() 
void insert(int val);
void remove(int val);

private:
node* insertInto(node* root,int val);
node* removeFrom(node* root,int val)

;

我尝试过像这样使用结构继承:

struct avlnode : node

    int height;
;

但这里的问题是 avlnode-&gt;leftavlnode-&gt;rightnode* 类型而不是 avlnode* 类型,因此我无法从它们访问 height 成员。

任何帮助将不胜感激:D

【问题讨论】:

所以基本上你想为int heightOf(node* root);函数引入一个等效的字段......我会说,不要这样做,在需要时使用该函数,所以你永远不必担心更改树时的过时高度。 而不是重新定义,在AVL 中派生node 应该可以工作,即struct Node: BST::Node 在类AVL 中。 BST 不需要知道节点的“扩展”。当必须复制节点时,您必须小心,但恕我直言,这在树中不是必需的。如果您想访问节点,那么您当然可以应用向上转换,例如(AVL::node*)static_cast&lt;AVL::node*&gt;()。 (dynamic_cast&lt;&gt;() 没有任何虚拟就无法工作。) 但是计算高度需要 O(n) 时间。而在AVL旋转中,我们需要在每一步中找到一个节点的高度,每次调用一个函数不仅会增加时间,还会导致重复计算。 您可能必须在class AVL 中重载class BST 的某些方法才能正确处理节点。在struct node 中引入类似虚拟显示方法(以及其他节点特定事物的类似方法)可以简化事情(并且可以使class BST 方法的重载变得不必要)。 可能,值得看看std::set的设计,其中节点(键)和less操作符作为模板参数提供。 AFAIK,std::set&lt;&gt; 在内部使用二叉树。 【参考方案1】:

在 OP 中,派生类被命名为 AVLTree。不幸的是,OP 不是 MCVE。 “令人兴奋的”实现细节被忽略(甚至尚未开发)。在回忆了 AVL Tree 的工作原理后,我决定忽略它。因此,我想简单地展示类的派生是如何工作的:

注意:我拆分示例代码以防止嵌套滚动框。

来源trees.cc

首先我要使用的标准库的一些头文件:

#include <iostream>
#include <iomanip>
#include <string>

基类BTree的声明:

class BTree 

从节点的嵌入类开始:

  // types:
  protected:
    struct Node 
      int value;
      Node *pLeft, *pRight;

      // constructor.
      Node(int value): value(value), pLeft(nullptr), pRight(nullptr)  
      // destructor.
      virtual ~Node()  delete pLeft; delete pRight; 
      // disabled:
      Node(const Node&) = delete;
      Node& operator=(const Node&) = delete;

      // prints node.
      virtual void print(std::ostream &out)
      
        out << "Node " << value;
      
    ;

...变量...

  // variables:
  protected:
    Node *_pRoot;

...和方法。

  // methods:
  public:
    // constructor.
    BTree(): _pRoot(nullptr)  
    // destructor.
    ~BTree()  delete _pRoot; 
    // disabled:
    BTree(const BTree&) = delete;
    BTree& operator=(const BTree&) = delete;

    // inserts a node.
    bool insert(int value)  return insert(_pRoot, value); 
    // prints the tree.
    void print(std::ostream &out, bool inOrder)
    
      if (_pRoot) 
        if (inOrder) printInfix(out, _pRoot, 0);
        else print(out, _pRoot, 0);
       else out << "EMPTY." << std::endl;
    

  // internal methods:
  protected:
    // creates and inserts a node.
    bool insert(Node *&pNode, int value);
    // prints a sub-tree.
    void print(std::ostream &out, Node *pNode, int indent);
    // prints a sub-tree in order.
    void printInfix(std::ostream &out, Node *pNode, int indent);

;

内部方法的实现:

bool BTree::insert(Node *&pNode, int value)

  if (!pNode) 
    pNode = new Node(value);
    return true;
  
  if (value == pNode->value) return false; // ERROR!
  return insert(
    value < pNode->value ? pNode->pLeft : pNode->pRight, value);


void BTree::print(std::ostream &out, Node *pNode, int indent)

  out << std::setw(indent) << "";
  pNode->print(out);
  out << std::endl;
  indent += 2;
  if (pNode->pLeft) print(out, pNode->pLeft, indent);
  if (pNode->pRight) print(out, pNode->pRight, indent);


void BTree::printInfix(std::ostream &out, Node *pNode, int indent)

  if (pNode->pLeft) printInfix(out, pNode->pLeft, indent + 2);
  out << std::setw(indent) << "";
  pNode->print(out);
  out << std::endl;
  if (pNode->pRight) printInfix(out, pNode->pRight, indent + 2);

第二个nd树的派生类:

class BTree2: public BTree 

从派生节点的嵌入类开始:

  // types:
  protected:
    struct Node: public BTree::Node 
      int height;

      // constructor.
      Node(int value, int height):
        BTree::Node(value), height(height)
       
      virtual ~Node() = default;
      // disabled:
      Node(const Node&) = delete;
      Node& operator=(const Node&) = delete;

      // prints node.
      virtual void print(std::ostream &out)
      
        out << "Node " << value << " (height: " << height << ")";
      
    ;

...没有变量,但有一些派生方法...

  // methods:
  public:
    // constructor.
    BTree2(): BTree()  
    // destructor.
    ~BTree2() = default;
    // disabled:
    BTree2(const BTree2&) = delete;
    BTree2& operator=(const BTree2&) = delete;

    // inserts a node.
    bool insert(int value)
    
      return insert((Node*&)_pRoot, value, 0);
    

  // internal methods:
  protected:
    // creates and inserts a node.
    bool insert(Node *&pNode, int value, int height);
;

...和实现:

bool BTree2::insert(Node *&pNode, int value, int height)

  if (!pNode) 
    pNode = new Node(value, height);
    return true;
  
  if (value == pNode->value) return false; // ERROR!
  return insert(
    (Node*&)(value < pNode->value ? pNode->pLeft : pNode->pRight),
    value, pNode->height + 1);

最后但并非最不重要的一个小测试的主要功能:

// main function
int main()

  // some test data
  int data[] =  3, 7, 21, 2, 12, 1, 104, 13 ;
  enum  nData = sizeof data / sizeof data[0] ;
  // binary tree
   std::cout << "Binary Tree:" << std::endl;
    BTree bTree;
    std::cout << "Build..." << std::endl;
    for (int value : data) bTree.insert(value);
    std::cout << "Print Hierarchy..." << std::endl;
    bTree.print(std::cout, false);
    std::cout << "Print Sorted..." << std::endl;
    bTree.print(std::cout, true);
    std::cout << "Destroy..." << std::endl;
    std::cout << std::endl;
  
  // derived binary tree
   std::cout << "Derived Binary Tree:" << std::endl;
    BTree2 bTree;
    std::cout << "Build..." << std::endl;
    for (int value : data) bTree.insert(value);
    std::cout << "Print Hierarchy..." << std::endl;
    bTree.print(std::cout, false);
    std::cout << "Print Sorted..." << std::endl;
    bTree.print(std::cout, true);
    std::cout << "Destroy..." << std::endl;
    std::cout << std::endl;
  
  // done
  return 0;

我将示例上传到 ideone.com

我在 Windows 10 的 cygwin 中使用 gcc 编译并测试了它:

$ g++ -std=c++11 -o trees trees.cc

$ ./trees.exe 
Binary Tree:
Build...
Print Hierarchy...
Node 3
  Node 2
    Node 1
  Node 7
    Node 21
      Node 12
        Node 13
      Node 104
Print Sorted...
    Node 1
  Node 2
Node 3
  Node 7
      Node 12
        Node 13
    Node 21
      Node 104
Destroy...

Derived Binary Tree:
Build...
Print Hierarchy...
Node 3 (height: 0)
  Node 2 (height: 1)
    Node 1 (height: 2)
  Node 7 (height: 1)
    Node 21 (height: 2)
      Node 12 (height: 3)
        Node 13 (height: 4)
      Node 104 (height: 3)
Print Sorted...
    Node 1 (height: 2)
  Node 2 (height: 1)
Node 3 (height: 0)
  Node 7 (height: 1)
      Node 12 (height: 3)
        Node 13 (height: 4)
    Node 21 (height: 2)
      Node 104 (height: 3)
Destroy...


$

注意事项:

    struct Node 中引入虚拟方法可以使用dynamic_cast&lt;&gt;()。 AFAIK,dynamic_cast&lt;&gt;() 只能应用于指针类型。因此,我只是做了一个 C-cast(即(Node*&amp;)),这是 C++ 纯粹主义者实际上不喜欢的。

    class BTree2insert() 不使用class BTree 的任何插入内容。这是确保树中的每个节点实际上都是BTree2::Node 所必需的。

    BTree::print() 不需要因为虚拟的Node::print() 方法而被重载。

这个设计只是展示了一些重用代码的方法。

模板只是一个不同的机会。标准库(例如std::set<>)提供了一个示例。

【讨论】:

【参考方案2】:

如果你使用结构继承,Derived 类会继承 struct 节点,你会得到一个 node* 而不是 avlnode* .

现在,您可能希望在派生类中重新定义结构 node,这将隐藏 Parent 中的 node 并更喜欢 'node' 在派生类中,但您在父类中使用的任何函数都将使用父类中 'node' 的定义。

也许您可以在父类的节点中声明一个高度变量并仅在派生类中使用它,或者在派生类中声明一个vector&lt;int&gt; heights并将高度存储在其中。

另外,在类之外声明 struct 节点,并使用它的实例作为类的成员。

【讨论】:

以上是关于从派生类重新定义基类中定义的结构的主要内容,如果未能解决你的问题,请参考以下文章

C++笔记-类层次结构

基类 派生类 类的继承与约束

使用基类中定义的函数返回指向派生类实例的指针时出现无效转换错误

虚函数和基类中的this指针的问题!

c ++:是不是可以在派生类中定义重载运算符,这将工作objs基类类型

我可以根据抽象基类中定义的某个属性创建派生类的实例吗?