Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Templates

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: [ 1 ] > 2 < [ 3 ]
010
21.01.2009, 03:28 Uhr
Suba Esel



Wer haette es gedacht - die naechste Frage

main.cpp:

C++:
#include "linked_list.hpp"

int main()
{
    LinkedList<std::string> asdf;
    asdf.add("asdf");
    asdf.print();
}



linked_list.hpp:

C++:

#include "list_node.hpp"
#include <iostream>

template <typename T>
class LinkedList
{
public:
    LinkedList();
    void add(T);
    void print();
private:
    typename ListNodePtr<T>::type root_;
};

template <typename T> void LinkedList<T>::print()
{
    typename ListNodePtr<T>::type current = root_;
    while(current)
    {
        std::cout << (*current) << std::endl;
        //std::cout << current->getContent() << std::endl;
        current = current->getNext();
    }
}
// Rest ist, denke ich mal, unerheblich



list_node.hpp:

C++:
#include <boost/shared_ptr.hpp>
#include <ostream>

template <typename T> class ListNode;

template <typename T> struct ListNodePtr
{
    typedef boost::shared_ptr<ListNode<T> > type;
};

template <typename T>
class ListNode
{
    friend std::ostream& operator<<(std::ostream&, const ListNode<T>&);
public:
    ListNode(T, typename ListNodePtr<T>::type);
    ListNode(T);
    typename ListNodePtr<T>::type getNext();
    void setNext(typename ListNodePtr<T>::type);
    T getContent();
private:
    typename ListNodePtr<T>::type next_;
    T content_;
};

template <typename T>
std::ostream& operator<<(std::ostream& stream, const ListNode<T>& node)
{
    return stream << node.content_;
}



Ersteinmal - macht das ueberhaupt Sinn, einen operator<< fuer ListNode zu definieren? (Ich find's einfach schoener so, aber das heisst ja nichts.)


Mein Problem ist jetzt - der Linker sagt mir "undefined reference to `operator<<(std::ostream&, ListNode<std::string> const&".
--
Simon

Dieser Post wurde am 21.01.2009 um 03:30 Uhr von Suba Esel editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
011
21.01.2009, 03:54 Uhr
0xdeadbeef
Gott
(Operator)



C++:
template<typename U>
friend std::ostream& operator<<(std::ostream&, const ListNode<U>&);


--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
012
21.01.2009, 04:04 Uhr
Suba Esel



EDIT: Zu bloede zum lesen, sry.

Perfekt, danke!

(Um nochmal auf meine andere Frage zurueckzukommen - macht das so ueberhaupt Sinn?)
--
Simon

Dieser Post wurde am 21.01.2009 um 04:07 Uhr von Suba Esel editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
013
21.01.2009, 04:39 Uhr
0xdeadbeef
Gott
(Operator)


Das ist eine Designfrage, die haben selten eindeutige Antworten - zumal die Sinnfrage hier schon deswegen nicht viel Sinn ergibt, weil der ganze Kram gestrichen und durch

C++:
#include <list>


ersetzt werden könnte.

Das vorweggeschoben, ich denke, dass der operator<< dann Sinn ergibt, wenn die Liste am Ende Listenknoten nach außen geben soll. Solange nur die Liste selbst mit den Listenknoten hantiert, ist es herzlich gleichgültig, ob du jetzt einen operator<< oder eine print-Methode oder was auch immer definierst.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
014
21.01.2009, 19:35 Uhr
Suba Esel



Naja nee, der Sinn der Sache ist ja nicht, eine Listenklasse zum Benutzen zu schreiben, sondern, etwas ueber templates zu lernen...

Das mit dem "gleichgueltig" leuchtet mir aber ein, danke!
--
Simon
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
015
21.01.2009, 21:04 Uhr
0xdeadbeef
Gott
(Operator)


Im Grunde greift hier, wie eigentlich immer, das KISS-Prinzip. Ginge es jetzt um ein großes, komplexes Backend (wie z.B. in einigen Boost-Bibliotheken), dann würde ich mir um interne Schnittstellen Gedanken machen, aber hier?

Im Zweifel ist es nachher sowieso am einfachsten, die Liste direkt in die Listenknoten greifen zu lassen. Natürlich kann man da einige Dinge so oder so modellieren; zum Beispiel könnte man das Umhängen der Zeiger als Funktionalität des Listenknotens auffassen und ihm entsprechende Methoden geben. Ich halte das aber eigentlich nicht für wirklich sinnvoll, weil die eigentlichen Operationen (Einfügen, Entfernen, etc.) in der Form eine Funktionalität mehrerer Listenknoten wären, was dann dreckiger zu implementieren sein dürfte, als es einfach als Funktionalität der Liste selbst aufzufassen.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
016
21.01.2009, 21:29 Uhr
~Suba Esel
Gast


Hm...
Meinst du im Prinzip, keine getNext / setNext / etc. zu schreiben, sondern next_ und content_ public zu machen?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
017
21.01.2009, 21:57 Uhr
0xdeadbeef
Gott
(Operator)


Ich würde für eine generische Liste den Knotentypen wohl als einfaches POD-Struct auffassen und alles weitere in der Liste machen.

Natürlich ist das nur dann sinnvoll, wenn die Knoten selbst nie nach außen gelangen. Wenn du vorhast, sie zum Beispiel als Iteratoren zu gebrauchen, dann brauchen sie natürlich auch ein entsprechendes Interface.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
018
22.01.2009, 00:41 Uhr
Suba Esel



Hm, ein next-Zeiger muesste aber immer noch vorhanden sein, falls ich in der Liste einen Vector (oder was auch immer) von Elementen anlege, wird das ganze sinnfrei.

Iteratoren - naja, mal als Beispiel meine add - Methode (kommt Iteratoren, glaube ich, ziemlich nahe):

C++:
template <typename T> void LinkedList<T>::add(T content)
{
    typename ListNodePtr<T>::type node(new ListNode<T>(content)), current;
    if(!root_)
        root_ = node;
    else
    {
        current = root_;
        while(current->getNext())
            current = current->getNext();
        current->setNext(node);
    }
}



Spaeter dann noch insert / delete, da muss ich auch ueber die ganzen Elemente rueber... oder was genau meintest du jetzt? Einfach statt current = current->getNext() current = current.next_ etc.?

Ach uebrigens - ich hoffe mal, ich nerve nicht zu sehr
--
Simon
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
019
22.01.2009, 00:55 Uhr
0xdeadbeef
Gott
(Operator)


Also, wenn ich eine einfach verkettete Liste mit Listenkopf schriebe, dann finge ich das wohl etwa so an (template-Kram der Übersichtlichkeit halber weggelassen):

C++:
struct list_node {
  list_node *next;
  int value;
};

class list {
public:
  inline std::size_t size() const { return size_; }
  void add(int x);

private:
  list_node *head_;
  std::size_t size_;
};

void list::add(int x) {
  list_node *node = new list_node;

  node->next = head_;
  node->value = x;

  head_ = node;
  ++size_;
}


Möglicherweise würde ich der list_node noch einen Konstruktor spendieren, aber das wär's dann auch.

Aber: Wenn ich jetzt die list_node nach außen geben will, dann muss ich für die Außenwelt ein brauchbares Interface zur Verfügung stellen, zum Beispiel so:

C++:
class list;

class list_node {
public:
  inline list_node       *next()       { return next_; }
  inline list_node const *next() const { return next_; }

  inline int value() const { return value_; }
  inline void value(int x) { value_ = x; }

private:
  friend class list;
  list_node *next_;
  int value_;
};

class list {
public:
  list_node *find(int x);
  list_node const *find(int x) const;

  void insert(list_node *pos, int x);
  // etc.

private:
  list_node *head_;
  std::size_t size_;
};



Und nerven tuste nicht, nein. Du fragst ja bloß Kram, um ihn besser zu verstehen, dafür habe ich sowohl Verständnis als auch Respekt.

Wenn du jetzt mit irgendeiner völlig albernen Idee herkämst, wie...weddicknich...eine rückwärts verkettete Liste, die trotzdem am Kopf angefasst wird oder so, und darauf bestündest, dass es sich um die tollste Idee der Welt handele, weil sie deinem Hirn entsprungen sei, dann sähe das vielleicht anders aus. *mit dem Zaunpfahl in Richtung eines anderen Threads wink*
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 22.01.2009 um 01:00 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: [ 1 ] > 2 < [ 3 ]     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: