【问题标题】:C++ Polymorphism with types具有类型的 C++ 多态性
【发布时间】:2014-08-31 02:17:22
【问题描述】:

我正在尝试拥有一个具有 T 类型变量的节点,T data; 以及存储指向其父节点 NodeBase *parent; 的指针。

类如下所示:

class Node: public NodeBase {
    T data;
    NodeBase *parent;
public:
    Node(T);
    void setData(T);
    T * getData(void);
    void setParent(NodeBase * const);
    NodeBase * getParent(void);
};

class NodeBase {
};

我以为我可以在NodeBase中放一个纯虚函数,但是你必须指定返回类型,因为NodeBase没有类型我不能指定virtual T * getData(void) = 0;

我遇到问题的具体案例:

Node<char> n1 = Node('A');
Node<int>  n2 = Node(63);
n1.setParent(&n2);

NodeBase *pNode = n1.getParent();
pNode->getData(); // Error: BaseNode has no member 'getData()'

【问题讨论】:

  • getData 必须是 nodebase 的虚拟成员函数 - 更重要的是它必须具有已知类型,以便对 NodeBase* p; p-&gt;getData(); 的每次调用都将返回相同的类型
  • 你能把NodeBase做成一个模板类吗?
  • 另外,NodeBase 的目的是什么?你会有多个实现吗?
  • @Code-Apprentice 即奇怪地重复出现的模板模式?这是一个很好/惯用的解决方案,但 OP 需要了解不同类型的两个节点不能被视为具有相同的基本类型。
  • @user3125280 OIC...OP 想要 Node&lt;char&gt;Node&lt;int&gt; 的通用基类。

标签: c++ templates inheritance polymorphism virtual


【解决方案1】:

我相信您将运行时多态性与编译时多态性混淆了。模板提供后者。解决您的问题的一种可能方法是使 NodeBase 成为模板类:

template <class T>
class NodeBase {
    public:
        virtual T getData() = 0;
};

template <class T>
class Node : NodeBase<T> {
    T data;
    NodeBase *parent;

public:
    Node(T);
    void setData(T);
    T getData(void);
    void setParent(NodeBase<T> * const);
    NodeBase<T> * getParent(void);
};

请注意,我让getData 返回T 而不是T*。 (有理由返回指针吗?)

【讨论】:

  • 这个问题是Node 不能有NodeBase *parent,其中父级与特定Node 的类型不同。例如,Node&lt;int&gt; 不能有 Node&lt;char&gt; 的父级。
  • @MichaelMitchell 没错。我不认为有办法做你想做的,因为getData() 的签名取决于你的模板类的参数。
  • @MichaelMitchell 那么您是说要将chars 和ints 存储在同一个列表中吗?你能提供一个用例吗?
  • 不,这些只是随机案例,我需要一个适用于多种类型的解决方案。
  • @MichaelMitchell 你能提供一个具体的例子吗?例如,“许多类型”是指“许多内置类型”还是“许多用户定义类型”? (对于后者,您可以创建继承层次结构。)
【解决方案2】:

C++ 是静态类型的,所以每次你写这样的代码:

NodeBase *pNode = n1.getParent();
pNode->getData(); // Error: BaseNode has no member 'getData()'

编译器需要知道getData() 的类型。在您的情况下,您希望它是 int(void)char(void) - 没有参数的函数返回 charint

有办法解决这个问题。该策略将取决于您的情况。返回类型有几种情况:

  • 一组固定的 bultins - 使用联合
  • 一组固定的内置函数或类,但类型永远不会改变 - 使用联合
  • 修复了一组内置函数或类,但类型可能会改变 - 使用 boost 变体
  • 任何其他情况 - 使用 boost any

所以总而言之,在返回上述数据结构之一的基类(非模板化)中添加一个虚函数。 boost 的文档很容易在网上找到。

这在 c++ 中要困难得多的原因是因为其他语言(尤其是动态语言)隐藏了困难。进行函数调用时,会分配空间 - 通常在堆栈上以获得最佳速度 - 并将返回值复制到该空间中。如果要返回未知类型,则需要使所有可能的返回类型(联合,也许是 boost 变体?)的最大可用空间,或者从堆中分配该空间(boost any)。许多静态类型语言只为前者提供良好的支持。

如果您想自己动手,C++ 确实有一些有用的功能。

#include <iostream>
#include <typeinfo>

using namespace std;

struct NodeBase {
    virtual ~NodeBase(){}
};

template<typename T>
struct Node: public NodeBase {
    T data;
    NodeBase *parent;

    Node(T data, NodeBase* parent = nullptr)
        : data(data), parent(parent)
    {}

    ~Node(){}
};

int main()
{
    Node<int>  n2(63);
    Node<char> n1('A', &n2);

    NodeBase* b = n1.parent; //pointer to n2

    if(typeid(*b) == typeid(Node<int>))
        cout << ((Node<int>*) b)->data << endl;

    return 0;
}

请注意,此功能需要 typeinfo 和 NodeBase 中的至少一个虚函数。

【讨论】:

  • 我更感兴趣的是学习如何而不是使用图书馆,但现在我有地方可以看看,谢谢。
  • @MichaelMitchell 在这种情况下,我将添加一个带有 typeid 运算符的示例
【解决方案3】:

你可以给NodeBase添加一个模板函数:

class NodeBase {
    public:
       template<typename T>
       T* getData();
};

这会将对象转换为Node&lt;T&gt; 并返回getData()。这是一个工作示例 - 仅包含因为它实际上花了我一段时间来解决循环定义的问题:

class NodeBase {
    public:
    template<typename T>
    T* getData();
};

template<typename T>
class Node: public NodeBase {
    T data;
    NodeBase *parent;

protected:  
    friend NodeBase;
    T * getData(void){return &data;};

public:
    Node(T var){data=var;};
    void setData(T& var){data=var;};
    void setParent(NodeBase * const par){parent=par;};
    NodeBase * getParent(void){return parent;};
};

template<typename T>
T* NodeBase::getData(){ return dynamic_cast<Node<T>*>(this)->getData();};


int main(){
    Node<char> n1 = Node<char>('A');
    Node<int>  n2 = Node<int>(63);
    n1.setParent(&n2);

    NodeBase *pNode = n1.getParent();
    std::cout << *pNode->getData<int>() << std::endl;

    return 0;
}

【讨论】:

  • 但是如果 OP 在调用站点知道数据 pNode 的类型,那么他所需要的只是一个强制转换......这不是类型安全的,除非在微不足道的情况下。这段代码做了哪些演员没有做的事情?
  • @user3125280 它隐藏了类中的逻辑并且需要(不仅,而且需要)用户实际说出他需要什么类型。据我所知 - 它和演员表一样类型安全,所以你在一个评论中提到这两件事是很奇怪的。你能详细说明一下吗?我在下面看到你的答案,但我不明白为什么不能在这里应用typeid,让它变得安全?
  • 多态性意味着可以编写代码与对象的底层类型无关 - 通过查询类型 (typeid) 或忽略它 (virtual)。例如,OP 希望能够恢复存储在基础类型中的数据 - 但不能总是知道它的类型。你的回答能解决这个问题吗?你将如何编写一个通过任意NodeBase* 的函数? see an example here typeid 不是(恕我直言)解决方案的一部分,以后会“为了安全”。这是解决方案。这就是它存在的原因。
  • 也 reinterpret cast 是 plain wrong here "reinterpret_cast 不能用于在指向两个通过继承相关的不同类的指针之间进行转换" - 虽然很容易修复
  • @user3125280 “例如 OP 希望能够恢复存储在基础类型中的数据 - 但不能总是知道它的类型。你的答案是否解决了这个问题?”我认为 OP 最了解他的需求。由于这是现在公认的答案,我想这就是他所需要的。您对reinterpret_cast tho 的看法是正确的。会尽快改的。
【解决方案4】:
NodeBase *pNode = n1.getParent();
pNode->getData(); // Error: BaseNode has no member 'getData()'

你有pNode(子),所以你可以直接使用getData(),其次你的基类对getData()一无所知,所以使用向上转换的指针你不能调用它。

【讨论】:

    猜你喜欢
    • 2017-03-28
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多