使用C ++模板实现树

时间:2015-07-07 00:42:02

标签: c++ templates polymorphism

我试图了解C ++中的模板是如何工作的,所以我想用C ++模板实现一个树。这是一个传统的多态版本:

#include <iostream>
using namespace std;

class Node {
public:
    virtual ~Node() {}
    virtual void print() = 0;
};

class InnerNode : public Node {
public:
    InnerNode(Node *lhs, Node *rhs) {
        m_lhs = lhs;
        m_rhs = rhs;
    }

    virtual ~InnerNode() {
        delete m_lhs;
        delete m_rhs;
    }

    virtual void print() {
        cout << "inner" << endl;
        if (m_lhs != NULL)
            m_lhs->print();
        if (m_rhs != NULL)
            m_rhs->print();
    }
private:
    Node *m_lhs;
    Node *m_rhs;
};

class LeafNode : public Node {
    virtual void print() {
        cout << "leaf" << endl;
    }
};

int main() {
    Node *l1 = new LeafNode();
    Node *l2 = new LeafNode();
    Node *r = new InnerNode(l1, l2);
    r->print();
}

我正在尝试仅使用模板来实现此代码的版本,但我遇到了类似这样的问题:

#include <iostream>
using namespace std;

template <typename T>
class Node {
public:
    virtual ~Node() {}
    void print() {
        static_cast<T *>(this)->print();
    }
};

class InnerNode : public Node<InnerNode> {
public:
    InnerNode(Node *lhs, Node *rhs) {
        m_lhs = lhs;
        m_rhs = rhs;
    }

    virtual ~InnerNode() {
        delete m_lhs;
        delete m_rhs;
    }

    void print() {
        cout << "inner" << endl;
        if (m_lhs != NULL)
            m_lhs->print();
        if (m_rhs != NULL)
            m_rhs->print();
    }
private:
    Node *m_lhs;
    Node *m_rhs;
};

class LeafNode : public Node<LeafNode> {
    virtual void print() {
        cout << "leaf" << endl;
    }
};

int main() {
    Node *l1 = new LeafNode();
    Node *l2 = new LeafNode();
    Node *r = new InnerNode(l1, l2);
    r->print();
}

如何调整多态版本以改为使用模板?

2 个答案:

答案 0 :(得分:1)

模板允许您使用名称替换类型,并使用该类型生成代码的特定实例。例如,可以使用变量类型的节点。

您的节点&lt;&gt;模板永远不会引用模板类型参数,因此没有理由将其作为模板。本质上你的节点&#34; class只是一个抽象的接口,而不是一个模板。 InnerNode也不需要是一个模板,因为没有不同的类型,因为你只需要将基类指针存储到叶子。

叶子具有数据,因此是被模板化的候选者,但是由于您的示例在叶子节点中没有数据,因此模板类型的应用不明显。

希望这能说明:

#include <iostream>
using namespace std;

// interface for interacting with general nodes
class INode {
public:
    virtual ~INode() {}
    virtual void print() = 0;
};

class InnerNode : public INode {

public:
    InnerNode(INode *lhs, INode *rhs) {
        m_lhs = lhs;
        m_rhs = rhs;
    }

    virtual ~InnerNode() {
        delete m_lhs;
        delete m_rhs;
    }

    void print() {
        cout << "inner" << endl;
        if (m_lhs != NULL)
            m_lhs->print();
        if (m_rhs != NULL)
            m_rhs->print();
    }
private:
    INode *m_lhs;
    INode *m_rhs;
};

template< typename DataType >
class LeafNode : public INode {
    virtual ~LeafNode() {};
    public:
    LeafNode(DataType data) : m_data(data) {}

    virtual void print() {
        cout << "leaf:" << endl;
        cout << m_data << endl;
    }
    private:
    DataType m_data;
};

int main() {
    INode *l1 = new LeafNode<int>(2);
    INode *l2 = new LeafNode<float>(3.14);
    INode *r = new InnerNode(l1, l2);
    r->print();
    delete r;
}

http://cpp.sh/3rmq

答案 1 :(得分:-2)

您的问题预先假定树是保存数据的数据结构。该数据是模板将处理的类型。如果您没有任何数据,那么您没有一个数据类型来概括(模板)。

由于您询问了模板,我假设您有一棵树,它跟踪“某些数据类型”,我们将其称为myDataType。假设它是一个字符串。这是一个绝对最小的树接口,没有任何实现细节。我知道这是一个非常无用的树,但问题是模板。

typedef myData std::string;

class tree {
private:
    struct node {
        myData data;
        node * left, right;
    };
    node * head;
public:
    void add (myData value);
    bool is_value_in_the_tree (const myData &value);
};

如果为内部和叶子节点使用不同的节点类型,那没问题。这是写一棵树的一部分。实施细节。我不在乎。只要树适用于一种数据类型,转到模板就可以使其适用于任何数据类型。 (1)

所有努力工作都是为了实际制作树。转到模板,以便它可以使用任何数据类型,而不仅仅是“myData”很容易。只需说模板&lt; typename myData&gt;这样你就可以使用不同的类型,而不需要typedef,它只能是一种类型。或者,像其他人一样使用“T”:

template<typename T>
class tree {
private:
    template<typename T>
    struct node {
        T data;
        node<T> * left, right;
    };
    node<T> * head;
public:
    void add(T value);
    bool is_value_in_the_tree(const T &value);
};

请注意,由于树和节点现在是模板,因此您必须指定使用它们时的类型,尽管它可以是不同树的不同类型。树将是一棵树。

tree<std::string> t;
assert(!t.is_value_in_the_tree("hello");
t.add("hello")
assert(t.is_value_in_the_tree("hello");

另请注意,节点是私有的,并使用相同的T.您不必将它们设为私有,但我会这样做。但是让它们使用相同的类型是至关重要的,因为如果有人声明树,节点将处理某种类型。

注意1:好的,显然树不适用于任何类型,但是如果类型缺少树所需的类型,如运算符&lt;()或某些节点构造函数,则会出现链接器错误,具体取决于关于如何实现树。