2012-01-11 3 views
2

Я создал класс шаблона для узла в связанном списке, и я пытаюсь его выводить его содержимое в выходной поток, перегружая < <. Однако мой текущий код:Перегрузка << оператор в классах шаблонов

#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, но до сих пор нет костей ... Есть ли способ, чтобы динамически выяснить, какой тип шаблона используется и литым или что-то перед вставкой? Я знаю, что могу сделать тонну избыточного кода для всех примитивов, но это кажется расточительным и ненужным.

Если это поможет любому, я компиляции на Arch Linux x64 с GCC v4.6.2 20111223

Edit: Так много людей упоминания о нем. Я также попытался поставить класс снаружи в качестве друга и как автономную функцию, ни одна из которых не работает, потому что поток выдает адрес, а не данные, независимо от того, где я его помещал. Нет доступа к личным значениям данных, так что это нормально, чтобы он не был другом.

Edit: Тестовый пример: http://ideone.com/a99u5 также обновленный источник выше.

Редактировать: Добавлена ​​оставшаяся часть моего кода, чтобы помочь Аарону в его понимании кода.

+1

То, как вы это сделали, требует 'SomeNode << cout', что явно не то, что вы имели в виду. 'ostream & operator <<' должен всегда быть свободной функцией, потому что вы хотите «ostream» на LHS. Фиксирование, которое не решит вашу проблему обязательно, но ... –

+0

Производите тестовую проверку, пожалуйста.http://ideone.com –

ответ

4

Ваш код объявляет 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?

Edit: Теперь, когда мы можем увидеть TestCase, кажется, предположения о том, как operator<< называли правильно. Выход должен быть изменен на:

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

на самом деле вызвать operator<< для Node, вместо родового один для T*

+0

Я знаю, что есть несколько представлений связанного списка на interwebs и в стандартных библиотеках, но я просто пытался это сделать, чтобы я мог контролировать, как он функционирует ради моего личного понимания моей собственной структуры данных. – TechWiz

+0

... Ничего себе такая глупая ошибка ... Я чувствую себя таким начинающим ха-ха. Благодарю вас. – TechWiz

+1

@TechWiz: Не держите это чувство дорогим, он снова вернется к вам снова. ;) – Xeo

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; 
} 
+0

Я пробовал это ранее (и снова, как только вы повторно упомянули его на всякий случай), но это же дело. – TechWiz

3

Почему вы добавления template<class NType> перед вашим методом уже шаблонного класса?

Как правило, лучший способ перегрузки оператора < <, чтобы сделать его друга:

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

Edit: Для того, чтобы ответить на комментарий ниже, это то, что я имею в виду:

При определении шаблон класса в заголовке, вы не переобъявить этот шаблон для функций-членов класса:

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) реализации.

+0

Я не следую. Вы также помещаете 'template ' infront вашего класса? Также это возвращает адреса. Я пропустил что-то еще здесь? – TechWiz

+0

Извините, я получил немного неряшливо с моей копией и вставкой. Функция была объявлена ​​в определении класса и определена вне класса. Но ради краткости я переместил его в класс. Это была явно глупая идея, поскольку она создавала больше путаницы, которая, как должно быть, кажется. – TechWiz

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; 
} 

n1 и n2 являются указатели, а не объекты. Вы должны быть написаны:

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; 
} 
+0

Да, это была проблема, спасибо. Dang Java tryna внушает ужасные привычки во мне. – TechWiz

0

Другие указывали на то, что вам нужно использовать cout << *n1 вместо cout << n1, но есть еще одна важная ошибка в коде.

Ваш деструктор включает в себя вызов deletePointer(&data);, где data является членом класса. Это опасно. Если бы эта строка кода была выполнена, программа, вероятно, потерпит крах. data - это только одна небольшая часть объекта, на который указывает this, пытаясь удалить его, как попытка удалить один элемент в массиве - ints.

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).

+0

Деконструктор проверяет данные указателя перед вызовом метода deletePointer, поэтому, если данные не являются указателями, деконструктор не будет вызывать функцию. Также сообщение delete для удаления переменной-члена из-за пределов собственного кода деконструкции кажется чем-то, что должно быть ошибкой, независимо от того, был ли он правильно составлен код или нет. Это просто плохое программирование. Деконструктор также не должен вызываться напрямую, но должен быть частью сбора мусора, когда программа закрывается, и эти узлы являются частью чего-то большего, поэтому его просто не нужно использовать таким образом. – TechWiz

+0

@TechWiz, это не правильно, извините. Поясним. Даже если 'data' ** является ** типом указателя, его следует удалить с помощью' delete data', а не с 'delete & data'. –

+0

Также @TechWiz, это C++, а не Java. Вы сказали: «... но должен быть частью сбора мусора». –

Смежные вопросы