0

假设我对 List 和 Node 有以下定义:


template <class T> 
class List {
    public:
        class Iterator;
        class ConstIterator;

        //Constructors and Destructors.
        List() : head(NULL), tail(NULL), size(0) {}
        List(const List& list);
        ~List();

        //Methods
        Iterator begin();
        ConstIterator begin() const;
        Iterator end();
        ConstIterator end() const;
        void insert(const T& data);
        void insert(const T& data, const Iterator& iterator);
        void remove(const Iterator& iterator);
        int getSize() const;
        Iterator find(const T& item);
        ConstIterator find(const T& item) const;
        void sort();

        //Operators
        List operator = (const List& list);


    private:
        class Node;
        Node* head;
        Node* tail;
        int size;
};


template <class T>
class List<T>::Node
{
    public:
        //Constructors and destructors
        Node(const T& _data, const Node* _next) : data(_data), next(_next) {}       
        ~Node(); //Destructor 

        //Methods


        //Operators
        Node operator = (const Node& node);

    private:
        T data;
        Node* next;
};

我正在编写一个函数来将数据插入到这样的列表中:


    template<class T>
    void List<T>::insert(const T& data)
    {
    Node newNode = new Node(data, NULL);

    if (head == NULL)
    {
        head = &newNode;
        tail = &newNode;
    }
    else
    {
        (*tail)->next = &newNode;
        tail = &newNode;
    }
    size++;
}

但是我觉得奇怪的是,如果我换成(*tail)->next = &newNode;(*tail).next = &newNode;仍然可以编译。为什么,正确的做法是什么?

4

4 回答 4

1

您的类的定义可以(为了这个问题的目的)简化为:

class List {
    ...
    private:
        Node* head;
        Node* tail;
};

class Node {
    ...
    private:
        Node* next;
};

现在在你的List::insert方法中:

Node newNode = new Node(data, NULL);
(*tail)->next = &newNode;

...当您使用new表达式时,结果将是指向新分配的内存的指针。
你应该做的是:

Node* newNode = new Node(data, NULL);
tail->next = newNode;                // <-- equivalent to (*tail).next = newNode;
于 2013-09-18T17:20:52.287 回答
0

-> 运算符将自动为您取消引用指针,然后调用右侧的方法。所以:

tail->next

也可以,但是

tail.next

不会,因为 tail 是一个指针。要使用 . 运算符,您必须首先定义指针,如

(*tail).next

(*tail)

把你的指针变成一个对象。此时您可以使用 -> 或 .

一种 。不适用于指针,但 -> 会。

通常,为了便于键入,我使用 -> 因为它比使用 (*) 将指针转换为对象更短,这样我就可以使用点,但它们是等效的操作。

于 2013-09-18T17:12:20.887 回答
0

使用 Node->tail 是 (*Node).tail 的缩写形式。两种形式都有效。奇怪的是你说 (*Node)->tail 编译的事实。为此,必须将 Node 定义为双指针,即:

Node **tail;

但是您的代码中还有其他一些错误。在这一行中:

Node newNode = new Node(data, NULL);

您正在定义一个本地对象并为其分配动态内存。正确的方法是:

Node *newNode = new Node(data, NULL);  // defining it as a pointer

而不是评估为:

head = &newNode;

做:

head = newNode;

最后一点,考虑使用智能指针而不是原始指针。前者比后者更安全

于 2013-09-18T17:18:36.133 回答
0

你已经注意到了,(*tail)->next = &newNode并且(*tail).next = &newNode都编译了,这让你觉得很奇怪。

但不知何故,您可能还注意到这一行也可以编译!

Node newNode = new Node(data, NULL);

是你应该让你停下来的事情。

您在此处的模板中。很多东西“编译”。

您是否尝试实例化模板?

附录:

在这里只是为了向您展示事情有多疯狂,请查看此程序:

#include <iostream>
using namespace std;

template <class T>
class C {
  void f();
};

template <class T>
void C<T>::f() {
  int x = new int;
}

int main() {
  std::cout << "Hello, world\n";
}

现在检查一下:

$ g++ template-example.cpp && ./a.out 
Hello, world

但现在注意

#include <iostream>
using namespace std;
int main() {
  int x = new int;
  std::cout << "Hello, world\n";
}

产生:

$ g++ hello.cpp
hello.cpp: In function ‘int main()’:
hello.cpp:4: error: invalid conversion from ‘int*’ to ‘int’

TL;DR:当您在模板中时,有时不应该编译的东西“做”! (他们并没有真正编译——YET!)

于 2013-09-18T17:23:31.253 回答