Шаблоны C++ - LinkedList
EDIT -- ответил ниже, пропустил угловые скобки. Спасибо всем.
Я пытался написать рудиментарный односвязный список, который я могу использовать в других программах. Я хочу, чтобы он мог работать со встроенными и пользовательскими типами, то есть он должен быть шаблонным.
из-за этого мой узел также должен быть шаблонным, так как я не знаю, какую информацию он собирается хранить. Я написал класс node следующим образом -
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;
}
Я получаю ошибку при компиляции, однако - ошибка C2955: 'Node': Использование шаблона класса требует списка аргументов шаблона - что указывает мне на следующую строку кода в моем методе добавления -
Node* temp = new Node<T>;
Я не поймите, почему у этого нет информации о типе, так как он был передан в связанный список при создании в моем классе драйверов. что я должен делать, чтобы передать информацию типа узла?
должен ли я создать структуру частного узла вместо отдельного класса и объединить методы обоих классов в одном файле? Я не уверен, что это преодолеет проблему, но я думаю, что это может быть. Я бы предпочел иметь отдельные классы, если это возможно.
спасибо, Эндрю.
9 ответов
стоит
Node<T>* temp = new Node<T>;
кроме того, чтобы получить подсказки о том, как проектировать список, вы можете, конечно, посмотреть на std::list, хотя иногда это может быть немного сложно.
в то время как ответы уже были предоставлены, я думаю, что добавлю свое зерно соли.
при разработке класса шаблонов, это хорошая идея, чтобы не повторять аргументы шаблона почти везде, просто в случае, если вы хотите (один день) изменить конкретную деталь. В общем, это делается с помощью определения типов.
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;
};
также лучше определить методы вне объявления класса, что облегчает чтение взаимодействие.
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
затем выполните кучу операций, если какие-либо броски, у вас утечка памяти - the
setNextNull
вызов не должен быть необходимым: конструктор изNode
должен инициализировать все члены данных до значений meaningfull, включая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
требуется не выделять никакой памяти, таким образом, ничего не просочилось, и мы еще не выполнили никакой операции. Наши!--12--> метод теперь устойчив.
финал вопрос:
обычно 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
метод на традиционных векторах и списках ?
эта строка должна гласить:
Node<T>* temp = new Node<T>;
то же самое для next
указатель в классе Node.
как сказано, решение
Node<T>* temp = new Node<T>;
... потому что Node
сам по себе не является типом, Node<T>
есть.
вам нужно:
Node<T> *temp = new Node<T>;
может быть стоит typedef NodeType = Node<T>
на CustomLinkedList
класс, чтобы предотвратить повторное появление этой проблемы.
// file: main.cc
#include "linkedlist.h"
int main(int argc, char *argv[]) {
LinkedList<int> list;
for(int i = 1; i < 10; i++) list.add(i);
list.print();
}
// file: node.h
#ifndef _NODE_H
#define _NODE_H
template<typename T> class LinkedList;
template<typename T>class Node {
friend class LinkedList<T>;
public:
Node(T data = 0, Node<T> *next = 0)
: data(data), next(next)
{ /* vacio */ }
private:
T data;
Node<T> *next;
};
#endif//_NODE_H
// file: linkedlist.h
#ifndef _LINKEDLIST_H
#define _LINKEDLIST_H
#include <iostream>
using namespace std;
#include "node.h"
template<typename T> class LinkedList {
public:
LinkedList();
~LinkedList();
void add(T);
void print();
private:
Node<T> *head;
Node<T> *tail;
};
#endif//_LINKEDLIST_H
template<typename T>LinkedList<T>::LinkedList()
: head(0), tail(0)
{ /* empty */ }
template<typename T>LinkedList<T>::~LinkedList() {
if(head) {
Node<T> *p = head;
Node<T> *q = 0;
while(p) {
q = p;
p = p->next;
delete q;
}
cout << endl;
}
}
template<typename T>LinkedList<T>::void add(T info) {
if(head) {
tail->next = new Node<T>(info);
tail = tail->next;
} else {
head = tail = new Node<T>(info);
}
}
template<typename T>LinkedList<T>::void print() {
if(head) {
Node<T> *p = head;
while(p) {
cout << p->data << "-> ";
p = p->next;
}
cout << endl;
}
}
#include<iostream>
using namespace std;
template < class data > class node {
private :
data t;
node<data > *ptr;
public:
node() {
ptr = NULL;
}
data get_data() {
return t;
}
void set_data(data d) {
t = d;
}
void set_ptr(node<data > *p) {
ptr = p;
}
node * get_ptr() {
return ptr;
}
};
template <class data > node < data > * add_at_last(data d , node<data > *start) {
node< data > *temp , *p = start;
temp = new node<data>();
temp->set_data(d);
temp->set_ptr(NULL);
if(!start) {
start = temp;
return temp;
}
else {
while(p->get_ptr()) {
p = p->get_ptr();
}
p->set_ptr(temp);
}
}
template < class data > void display(node< data > *start) {
node< data > *temp;
temp = start;
while(temp != NULL) {
cout<<temp->get_data()<<" ";
temp = temp->get_ptr();
}
cout<<endl;
}
template <class data > node < data > * reverse_list(node<data > * start) {
node< data > *p = start , *q = NULL , *r = NULL;
while(p->get_ptr()) {
q = p;
p = p->get_ptr();
q->set_ptr(r);
r = q;
}
p->set_ptr(r);
return p;
}
int main() {
node < int > *start;
for(int i =0 ; i < 10 ; i ++) {
if(!i) {
start = add_at_last(i , start);
}
else {
add_at_last(i , start);
}
}
display(start);
start = reverse_list(start);
cout<<endl<<"reverse list is"<<endl<<endl;
display(start);
}