编辑——在下面回答,错过了斜括号。谢谢大家。
我一直在尝试编写一个基本的单链表,我可以在其他程序中使用它。我希望它能够使用内置和用户定义的类型,这意味着它必须是模板化的。
因此我的节点也必须被模板化,因为我不知道它要存储的信息。我写了一个节点类如下 -
template <class T> class Node
{
T data; //the object information
Node* next; //pointer to the next node element
public:
//Methods omitted for brevity
};
我的链表类是在一个单独的类中实现的,需要在将新节点添加到列表末尾时实例化一个节点。我已经实现了如下 -
#include <iostream>
#include "Node.h"
using namespace std;
template <class T> class CustomLinkedList
{
Node<T> *head, *tail;
public:
CustomLinkedList()
{
head = NULL;
tail = NULL;
}
~CustomLinkedList()
{
}
//Method adds info to the end of the list
void add(T info)
{
if(head == NULL) //if our list is currently empty
{
head = new Node<T>; //Create new node of type T
head->setData(info);
tail = head;
}
else //if not empty add to the end and move the tail
{
Node* temp = new Node<T>;
temp->setData(info);
temp->setNextNull();
tail->setNext(temp);
tail = tail->getNext();
}
}
//print method omitted
};
我已经设置了一个驱动/测试类如下 -
#include "CustomLinkedList.h"
using namespace std;
int main()
{
CustomLinkedList<int> firstList;
firstList.add(32);
firstList.printlist();
//Pause the program until input is received
int i;
cin >> i;
return 0;
}
但是我在编译时遇到错误 - error C2955: 'Node' : use of class template requires template argument list - 这指向我添加方法中的以下代码行 -
Node* temp = new Node<T>;
我不明白为什么它没有关于类型的信息,因为它是在我的驱动程序类中创建时传递给链表的。 我应该怎么做才能将类型信息传递给 Node?strong>
我应该创建一个私有(private)节点结构而不是一个单独的类,并将两个类的方法组合在一个文件中吗?我不确定这是否会解决问题,但我认为可能会。如果可能的话,我宁愿有单独的类(class)。
谢谢,安德鲁。
最佳答案
虽然已经提供了答案,但我想我会加一点盐。
在设计模板类时,最好不要在几乎所有地方重复模板参数,以防万一您希望(有一天)更改特定细节。通常,这是通过使用 typedef 来完成的。
template <class T>
class Node
{
public:
// bunch of types
typedef T value_type;
typedef T& reference_type;
typedef T const& const_reference_type;
typedef T* pointer_type;
typedef T const* const_pointer_type;
// From now on, T should never appear
private:
value_type m_value;
Node* m_next;
};
template <class T>
class List
{
// private, no need to expose implementation
typedef Node<T> node_type;
// From now on, T should never appear
typedef node_type* node_pointer;
public:
typedef typename node_type::value_type value_type;
typedef typename node_type::reference_type reference_type;
typedef typename node_type::const_reference_type const_reference_type;
// ...
void add(value_type info);
private:
node_pointer m_head, m_tail;
};
最好在类声明之外定义方法,这样更容易阅读接口(interface)。
template <class T>
void List<T>::add(value_type info)
{
if(head == NULL) //if our list is currently empty
{
head = new node_type;
head->setData(info);
tail = head;
}
else //if not empty add to the end and move the tail
{
Node* temp = new node_type;
temp->setData(info);
temp->setNextNull();
tail->setNext(temp);
tail = tail->getNext();
}
}
现在,几点说明:
- 如果
List<T>::add
会更友好正在向新添加的对象返回迭代器,例如insert
STL 中的方法(您也可以将其重命名为 insert) - 在执行
List<T>::add
您将内存分配给temp
然后执行一堆操作,如果有抛出,你已经泄漏了内存 setNextNull
不需要调用:Node
的构造函数应该将所有数据成员初始化为有意义的值,包括m_next
所以这里是修改后的版本:
template <class T>
Node<T>::Node(value_type info): m_value(info), m_next(NULL) {}
template <class T>
typename List<T>::iterator insert(value_type info)
{
if (m_head == NULL)
{
m_head = new node_type(info);
m_tail = m_head;
return iterator(m_tail);
}
else
{
m_tail.setNext(new node_type(info));
node_pointer temp = m_tail;
m_tail = temp.getNext();
return iterator(temp);
}
}
请注意使用适当的构造函数这一简单事实如何提高我们的异常安全性:如果在构造函数期间抛出任何东西,new
要求不分配任何内存,因此没有泄漏,我们还没有执行任何操作。我们的List<T>::insert
方法现在具有弹性。
最后一个问题:
通常insert
单链表的方法在开头插入,因为这样更容易:
template <class T>
typename List<T>::iterator insert(value_type info)
{
m_head = new node_type(info, m_head); // if this throws, m_head is left unmodified
return iterator(m_head);
}
您确定要在末尾添加插入内容吗?或者你这样做是因为 push_back
传统 vector 和列表的方法?
关于C++ 模板 - 链表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2079296/