重载<<模板类的运算符

时间:2012-01-11 02:20:03

标签: c++ templates operator-overloading

我为链表中的节点创建了一个模板类,我试图让它通过重载<<来将其内容输出到输出流。但是我目前的代码:

#include <iostream>
using namespace std;
template<class NType> class Node;

template<class NType>
class Node {
private:
        void deletePointer(NType* p);
public:
        NType data;
        Node *prev, *next;

        template<typename T>
        struct is_pointer { static const bool value = false; };

        template<typename T>
        struct is_pointer<T*> { static const bool value = true; };

        Node();
        Node(NType data);
        ~Node();
};  

int main() {
        Node<int> *n1 = new Node<int>();
        Node<int> *n2 = new Node<int>(10);

        std::cout << "Node 1: " << n1 << std::endl;
        std::cout << "Node 2: " << n2 << std::endl;
}

template<class NType> inline std::ostream & operator << (std::ostream& out, const Node<NType> &node){
        out << node.data;
        return out;
}

template<class NType> inline Node<NType>::Node()
            :data(NULL), prev(NULL), next(NULL)
{
}

template<class NType> inline Node<NType>::Node(NType data)
            :data(data), prev(NULL), next(NULL)
{
}

template<class NType> inline Node<NType>::~Node(){
        if(is_pointer<NType>::value){
                deletePointer(&data);
        } else {
                return;
        }
}

template<class NType> inline void Node<NType>::deletePointer(NType* p){
    delete p;
}

输出内存位置而不是节点内的数据。对于诸如int之类的原始类型,就好像它不知道NType容器中的数据类型一样。

Node 1: 0x741010
Node 2: 0x741030
Node 3: 0x741070
Node 4: 0x741090

我尝试使用typename而不是class,但仍然没有骰子......有没有办法动态找出模板在插入之前使用和投射的类型?我知道我可以为所有原语制作大量冗余代码,但这似乎是浪费和不必要的。

如果它有帮助,我正在使用GCC v4.6.2 20111223在Arch Linux x64上进行编译

编辑:由于很多人都提到它。我也尝试将这个类作为朋友和独立函数放在外面,因为无论我把它放在哪里,因为流输出地址而不是数据本身。没有私人数据值可供访问,因此可以不成为朋友。

修改: 测试用例:http://ideone.com/a99u5 还更新了上面的来源。

修改: 添加了我的代码的剩余部分,以帮助Aaron理解代码。

5 个答案:

答案 0 :(得分:7)

您的代码将operator<<声明为成员函数,因此它实际上将this指针作为第一个参数,ostream作为第二个参数。相反,它需要是一个自由功能:

template<class NType> class Node {
public:
    NType data;
    Node *prev, *next;
};
//Note how this is declared outside of the class body, so it is a free function instead of a memberfunction
template<class NType> inline std::ostream& operator<<(std::ostream& out, const Node<NType>& val){
    out << val.data;
    return out;
}

但是,如果您的operator<<需要访问私人数据,则需要将其声明为朋友函数:

template<class NType> class Node {
public:
    NType data;
    Node *prev, *next;
    friend std::ostream& operator<<(std::ostream& out, const Node& val){
        out << val.data;
        return out;
    }
};

现在输出:如果调用了operator<<,编译器会知道NType的类型,并在流式传输data成员时做正确的事。但是,由于你的operator<<不应该有效(如所写)并且它似乎给你memoryaddresses作为输出,我会假设你有类似以下内容:

Node* n = new Node();
std::cout<<n;
//When it should be:
std::cout<<*n;

现在只是出于好奇的缘故:为什么你自己实现看起来好奇的链接列表,而不是简单地使用std::list

修改 现在我们可以看到测试用例似乎关于如何调用operator<<的假设是正确的。输出需要更改为:

std::cout << "Node 1: " << *n1 << std::endl;
std::cout << "Node 2: " << *n2 << std::endl;

实际调用operator<<的{​​{1}},而不是Node

的通用版{}

答案 1 :(得分:3)

为什么要在已经模板化的类方法前添加template<class NType>

通常,重载运算符的最佳方法是&lt;&lt;是让它成为朋友:

template<typename NType>
friend std::ostream& operator<<(std::ostream& out, const Node<NType>& node)
{
    out << node.data;
    return out; 
}

编辑:要回复以下评论,这就是我的意思:

在标题中定义类模板时,不要为该类的成员函数重新声明该模板:

template<typename T>
class Foo
{
    T some_data;

    void member_fn();
}

当您声明非会员朋友功能时,这是必需的:

template<typename NType>
class Node 
{
public:

    NType data;
    Node<NType> *prev, *next;

    //Note the different template parameter!
    template<typename NType1>
    friend std::ostream& operator<<(std::ostream& out, const Node<NType1>& node);
};

然后实现上述template<typename NType> std::ostream& operator<<(std::ostream& out, const Node<NType>& node)实现。

答案 2 :(得分:1)

template <class NType>
class Node
{
  public:
    NType data;
    Node *prev, *next;
};

template <class NType>
inline std::ostream& operator<<(std::ostream& os, const Node<NType>& n)
{
    return out << n.data;
}

答案 3 :(得分:0)

您正在打印节点地址,而不是节点:

int main() {
        Node<int> *n1 = new Node<int>();
        Node<int> *n2 = new Node<int>(10);

        std::cout << "Node 1: " << n1 << std::endl;
        std::cout << "Node 2: " << n2 << std::endl;
}

n1n2指针,而不是对象。你应该写:

int main() {
        Node<int> *n1 = new Node<int>();
        Node<int> *n2 = new Node<int>(10);

        std::cout << "Node 1: " << *n1 << std::endl;
        std::cout << "Node 2: " << *n2 << std::endl;
}

或:

int main() {
        Node<int> n1();
        Node<int> n2(10);

        std::cout << "Node 1: " << n1 << std::endl;
        std::cout << "Node 2: " << n2 << std::endl;
}

答案 4 :(得分:0)

其他人指出您需要使用cout << *n1而不是cout << n1,但您的代码中还有另一个重要错误。

您的析构函数包含调用deletePointer(&data);,其中data是该类的成员。这很危险。如果执行了这行代码,那么程序可能会崩溃。 data只是this指向的对象的一小部分,尝试删除它就像尝试删除数组中的单个元素一样。

Node<int> *n1 = new Node<int>();
delete n1; // this makes sense. deleting the same thing that was new'ed

Node<int> *n1 = new Node<int>();
delete &(n1->data); // BUG

您可能应该更改设计并简单地删除指针的特殊代码。您是否希望编写如下代码:Node<int*> *n2 = new Node<int*>( new int );?如果是这样,你希望它如何表现?

如果NType实际上是指针类型,例如int*,则执行deletePointer(data)可能有意义,但执行deletePointer(&data)永远不会有意义。