【问题标题】:How do I implement polymorphism in a nested class?如何在嵌套类中实现多态性?
【发布时间】:2014-11-21 19:56:55
【问题描述】:

编辑:底部的具体示例

解释问题的最佳方式是通过示例(请注意,此特定示例的解决方案不是我要寻找的答案)。

假设我有一个基类Base,它基本上是一个容器:

class Base {
protected:
    class BaseNode {
    protected:
        BaseNode* next;
        int id;
    };
    Node* head;
public:
// ... functionality
};

现在我想派生和改进它,添加一个额外的数据字段:

class Derived : public Base {
private:
    class DerivedNode: public BaseNode {
        char* name;
    };
public:
// .. some more functionality
};

我想要的是一个具有Base 的所有功能的Derived 类,但让每个节点还包含一个name,而不仅仅是一个id

但如果我查看DerivedNode 的实例,我想要会是:

DerivedNode = {
    DerivedNode* next;
    int id;
    char* name;
};

但我拥有的是:

DerivedNode = {
    BaseNode* next;
    int id;
    char* name;
};

所以DerivedNode()->next->name 将是未定义的!

更不用说Base 类只处理BaseNodes...所有对newdelete 的调用都不会为DerivedNode 中的char* 分配足够的内存。

这似乎是一个非常普遍的问题,有没有一些巧妙的方法来解决这个问题? Bases 的大部分功能即使对于 DerivedNodes 也非常有用,所以我更愿意保持继承方案原样......

编辑:当这可能有用的具体示例

我真的不想写很多代码,所以我会试着解释一下……如果有人想看一个例子,我已经问了一个关于我的具体需求的问题here

我正在实现一个List 类。那是我的基类。它是模板化的(每个节点中的数据都是模板化的,每个节点也有 *next 和 *prev 字段),有一个迭代器嵌套类,通过迭代器位置插入、删除……作品。

我想实现一个Forrest 类,它是一个数据节点列表(仍然是模板化的),但每个节点都包含一个指向该节点的TreeNodes 列表。我想这样做是因为数据是可排序的,并且我希望能够在 log(n) 时间(因此是一棵树)中获取它,但它可以通过两种不同的方式(例如年龄和 ID 号)进行排序,所以我需要两棵树指向相同的数据。

所以:我让Forrest 继承自List,并派生ForrestNode 以包含指向数据的TreeNodes 列表:

class DerivedNode: public List<T>::ListNode {
    List<Tree<T*>::TreeNode*> treeNodes;
};

基类 List 不需要知道新的 treeNodes 字段,因为它与列表功能无关 - 像 swapget_headis_empty 这样的方法应该工作相同,并且创建一个新节点应该几乎相同(需要分配更多内存,但新的treeNodes 字段不应该用数据初始化)。

然而,新的 Forrest 类将覆盖基本的 Lists 方法,但仅限于添加功能 - 例如,在插入(常规列表插入)之后,指针将插入到相关树中,然后新树节点的地址将被添加到treeNodes列表中。

【问题讨论】:

  • 如果您真的需要多态性,这将非常困难,但由于您似乎没有使用多态性 (DerivedNode()-&gt;next-&gt;name),您可能想看看用模板解决这个问题。
  • 你能分享一些这样的设计会有帮助的具体场景吗?

标签: c++ inheritance polymorphism nested-class


【解决方案1】:

您可以简单地将 Base::BaseNode 转换为模板,并提供 next 成员变量的类型作为类型参数,如下所示:

class Base {
protected:
    template <class Derived>
    class BaseNode {
    protected:
        Derived* next;
        int id;
    };
    Node* head;
public:
// ... functionality
};

class Derived : public Base {
private:
    class DerivedNode: public BaseNode<DerivedNode> {
        char* name;
    };
public:
// .. some more functionality
};

【讨论】:

  • 如何解决分配问题?假设我在Base 中有一个insert 方法。它应该调用new BaseNode()...我将不得不重写Derived 中的insert 方法来调用new DerivedNode()
  • 你能举例说明这个插入方法实际上会做什么吗?它会在你的数据结构中插入什么数据?以及如何在不覆盖 Derived 中的 insert 方法的情况下将名称输入节点?
  • insert 方法会创建一个新节点并将其插入到 Base 类的头部(如列表),name 的默认构造函数对我来说已经足够了,我会写Derived 中设置/获取名称的方法
  • 如果不将 Base 转换为模板,我也看不到一个好的解决方案。如果您希望 Base 能够通用地处理节点,我认为没有办法解决这个问题。
【解决方案2】:

正如@stefaanv 所述,我们可以使用模板来实现您想要的。请验证以下程序:

#include <iostream>

using namespace std;


class Base {

protected:

    template<typename Z>
    class BaseNode{

    protected:

        Z* next;
        int id;
    };

public:

};


class DerivedNode;


class Derived : public Base{

private:

    class DerivedNode: public BaseNode<DerivedNode>{

        public:
        const char* name;

        void setId(int val){id = val;}
        void setNext(DerivedNode* nx){next = nx;}
        DerivedNode* getNext(){return next;}


    };

    DerivedNode* derHead;

public:


  Derived()
  {
      DerivedNode* node1 = new DerivedNode();

      node1->name = "Nik";
      node1->setId(1);
      node1->setNext(NULL);



      DerivedNode* node2 = new DerivedNode();

      node2->name = "Aka";
      node2->setId(2);
      node2->setNext(NULL);

      node1->setNext(node2);

      derHead = node1;
  }
  DerivedNode* getHead(){return derHead;}

  void print()
    {
       DerivedNode* tmp = derHead;
       while(tmp != NULL)
       {
           cout<<tmp->name<<endl;
           tmp = tmp->getNext();
       }

    }
};



int main()
{
   Derived obj;
   obj.print();

   return 0;
}

【讨论】:

  • 我在 cmets 中向@Kristian Duske 提出了这个问题:这将如何解决分配问题?具体来说,这个例子没有利用Base类的功能,你需要在Derived中写很多全新的方法,不是吗?
  • “不利用基类的功能”是什么意思。你知道你永远不能从外部修改受保护的成员。你只能通过子类来做到这一点,在这我刚刚描述了一种更新它们的方法。您可以使用一个更新功能代替多个功能。
猜你喜欢
  • 2016-05-06
  • 2017-06-11
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多