problem mit stack und basisklasse
-
Ja, genauer gesagt gibt die Funktion eine Referenz auf den Wert zurück, damit er auch verändert werden kann. Das würde ich sowieso so machen, damit du auch analog dazu bei den Queues eine Funktion schreiben kannst, die das letzte Element (d.h. eine Referenz darauf) zurückgibt.
Generell sind Funktionen besser, weil sie die Schnittstellen der Klassen repräsentieren. Dann kannst du die Zeiger auch privat haben. Und wenn du plötzlich noch eine weitere Funktionalität einbauen willst, kannst du die entsprechende Funktion ändern.
-
Hmm ich verstehe jetzt nicht genau was du meinst, ich schreib mal jetzt die 2 klassen, aber ohne funktionen:
template<class T> class LContainer { protected: class Element { public: T data; Element* next; Element(const T& v):data(v),next(NULL) {} //Copy C-tor von Element }; class EmptyLContainerException: public std::exception { virtual const char* what() throw() {return "LContainer is empty";} }; Element *ptrtofirst; unsigned int anz_elem; public: LContainer(); ~LContainer(); unsigned int getAnzahlElement(); bool is_empty(); void clear(); };template<class T> class LStack :public LContainer<T> { public: LStack(); LStack(const LStack& other); ~LStack(); void push(const T& v); T pop(); T& top(); void swap(LStack& other); LStack<T>& operator=(const LStack& other); };Dann kannst du ja genauer beschreiben was besser wäre^^
-
uhsuhz schrieb:
Dann kannst du ja genauer beschreiben was besser wäre^^
Ja, oder du könntest selber überlegen.
Ich hab ja alles schon gesagt: Schreib eine Funktion, die eine Referenz auf den Wert an der ersten Stelle zurückgibt.
-
hab ich doch in der LStack:
T& top();
oder wie meinst du das?
-
uhsuhz schrieb:
ich schreib mal jetzt die 2 klassen, aber ohne funktionen
Ups, da hab ich wohl nach deiner Mitteilung zu wenig genau hingeschaut. Dennoch hast du die Funktion nur deklariert, was dir noch gar nichts bringt

Definier die Funktion
top()und versuch doch einfach mal, eine Referenz auf den ersten Wert zurückzugeben. Wenn ich dir den Code poste, bringt das uns beiden nichts. Bei konkreten Problemen kannst du ja wieder nachfragen.
-
ach so wir haben aneinander vorbeigeredet^^ ich habe gemeint nur die deklaration der funktionen, die aber schon alle implementiert sind:
hier ist mal die ganze klasse:
#ifndef LSTACK_H #define LSTACK_H #include "lcontainer.h" template<class T> class LStack :public LContainer<T> { public: LStack(); LStack(const LStack& other); ~LStack(); void push(const T& v); T pop(); T& top(); void swap(LStack& other); LStack<T>& operator=(const LStack& other); }; template<class T>LStack<T>::LStack() :LContainer<T>::LContainer() { } template<class T>LStack<T>::LStack(const LStack &other) :LContainer<T>::LContainer() { if(!other.ptrtofirst) //leerer Stack, nichts zu kopieren return; this->ptrtofirst = new Element(other.ptrtofirst->data); //wenn hier was fliegt räumt C++ selber auf Element *rigth = other.ptrtofirst; Element *left = ptrtofirst; while(rigth->next) { try { left->next = new Element(rigth->next->data); //sollte hier was fliegen... rigth = rigth->next; left = left->next; } catch(...) { clear(); //müssen wir selber aufräumen throw; } } } template<class T>LStack<T>::~LStack() { LContainer::~LContainer(); } template<class T>void LStack<T>::push(const T& v) { Element* temp = new Element(v); temp->next = this->ptrtofirst; this->ptrtofirst = temp; ++anz_elem; } template<class T>T LStack<T>::pop() { if(!this->ptrtofirst) throw EmptyLContainerException(); T result(this->ptrtofirst->data); Element *temp = this->ptrtofirst; this->ptrtofirst = this->ptrtofirst->next; delete(temp); --anz_elem; return result; } template<class T>T& LStack<T>::top() { if(!this->ptrtofirst) throw EmptyLContainerException(); return this->ptrtofirst->data; } template<class T> void LStack<T>::swap(LStack &other) { std::swap(this->anz_elem, other.anz_elem); std::swap(this->ptrtofirst,other.ptrtofirst); } template<class T> LStack<T>& LStack<T>::operator=(const LStack& other) { LStack<T> temp = other; this->swap(temp); return *this; } #endif
-
ich habe noch eine Frage:
gibt es irgenteinen Sinn manche funktionen als konstantwährend zu deklarieren?
Für mich gibt es keinen Sinn zB: einen const Stack zu erstellen, oder was meint ihr?
-
Die
top()-Funktion sollte so eigentlich stimmen. Funktioniert sie? Und die restlichen Funktionen? Ich hab jetzt natürlich nicht deinen ganzen Code getestet. Aber schau mal, ob du keine Zugriffsverletzungen oder sonstige Fehler im Programm hast.Noch ein Detail: Es ist zwar Geschmackssache, aber du brauchst die
this->eigentlich nicht. Wenn sie dir bei der Übersichtlichkeit helfen, lass sie drin. Mich würden sie stören
uhsuhz schrieb:
gibt es irgenteinen Sinn manche funktionen als konstantwährend zu deklarieren?
Grundsätzlich schon. Erstens sieht man dann gerade (und stellt auch sicher), dass die Funktion die Klasse nicht verändert. Zweitens können wie gesagt auch konstante Instanzen diese Funktion benutzen.
Zu dem gerade:
uhsuhz schrieb:
Für mich gibt es keinen Sinn zB: einen const Stack zu erstellen, oder was meint ihr?
Nein, einen erstellen wohl kaum. Eher, wenn du bereits einen Stack hast, und von diesem eine konstante Kopie anfertigst. Oder wenn du eine konstante Referenz an eine Funktion übergibst.
-
ok danke, io die anderen funktionen funktionnieren alle, top klappt beim lesen auch schon mal hab noch nicht getestet, ob man auch schreiben kann.
zum dem this. Ich weiß dass man es nicht braucht, aber ich mag es irgentwie lieber wenn ich this-> davor schreibe.
nun habe ich noch ein frage, ich habe den == und den != operator in der basisklasse implementiert.
template<class T> class LContainer { protected: class Element { public: T data; Element* next; Element(const T& v):data(v),next(NULL) {} //Copy C-tor von Element }; class EmptyLContainerException: public std::exception { virtual const char* what() throw() {return "LContainer is empty";} }; Element *ptrtofirst; unsigned int anz_elem; public: LContainer(); ~LContainer(); unsigned int getAnzahlElement(); bool is_empty(); void clear(); bool operator==(LContainer& other); bool operator!=(LContainer& other); };template<class T>bool LContainer<T>::operator ==(LContainer& other) { if(this->anz_elem != other.anz_elem) return false; Element* rigth = other.ptrtofirst; Element* left = this->ptrtofirst; while(rigth) { if(rigth->data == left->data) { rigth = rigth->next; left = left->next; } else return false; } return true; } template<class T>bool LContainer<T>::operator!=(LContainer& other) { if(*this == other) return false; return true; }ich dachte dass ich die funktionen überladen müsste in der LStack klasse, und dann per static_cast<LContainer<T>>(...) aufrufen müsste, aber irgenwie klappt es auch wenn ich den operator nicht überladen habe für die LStack-klasse, kann ich das so lassen, oder ist es sicherer wenn ich per static_cast gehe?
-
Wie kommt man auf die Idee vor seine Klassen so ein bescheuertes L zu setzen?
-
öhm vielleicht weil das L für Linked steht?
aber dieser post war jetzt wirklick überflüssig, ich hätte die klasse ja auch
HaltDieFresseStack nennen können, ist ja meine entscheidung
-
uhsuhz schrieb:
ich dachte dass ich die funktionen überladen müsste in der LStack klasse, und dann per static_cast<LContainer<T>>(...) aufrufen müsste, aber irgenwie klappt es auch wenn ich den operator nicht überladen habe für die LStack-klasse, kann ich das so lassen, oder ist es sicherer wenn ich per static_cast gehe?
Ich würde die Operatoren global machen und in den Klassen als Friend deklarieren. Ich würde dann aber für jeden Containertypen, der in der Praxis vorkommt, eine überladene Funktion definieren. Nach Möglichkeit lässt du die Implementierung für die Basisklasse weg, damit es nicht zu ungewollten Vergleichen (implizite Umwandlung) kommt. Oder du machst das ganze in der Klasse (evtl. polymorph). Aber lass
op==für die Basisklasse weg, wenn er nicht gebraucht wird.Und den !=-Operator kannst du einfacher implementieren:
template<class T>bool LContainer<T>::operator!=(LContainer& other) { return !(*this == other); }Und right schreibt man so

-
Nexus schrieb:
Und right schreibt man so

lol hab da wohl nicht aufgepasst^^ na lieber mal schnell ändern^^
//Edit
hmm irgentwie verstehe ich nicht genau was ich da machen soll...:-( also aus der basisklasse rausnehmen ist ja klar, aber dann haben die ja keinen zugriff mehr auf element?? und auch nicht mehr auf ptrtofirst, ich müsste die funktion höchstens als friend deklarieren,hmm da bräuchte ich vielleicht wenn es geht genauere beschreibungen
-
Wenn du eine Klasse hast, und diese soll externen Funktionen Zugriff auf private Member gewähren, musst du diese Funktionen in der Klasse als Friend deklarieren.
// Deklaration und Definition der Funktion (genauer: des überladenen Operators) bool operator==(const MyClass& Left, const MyClass& Right) { // ... } class MyClass { // Friend-Deklaration innerhalb der Klasse; operator== kann jetzt // auf private Member von MyClass zugreifen friend bool operator==(const MyClass& Left, const MyClass& Right); };
-
uhsuhz schrieb:
öhm vielleicht weil das L für Linked steht?
aber dieser post war jetzt wirklick überflüssig, ich hätte die klasse ja auch
HaltDieFresseStack nennen können, ist ja meine entscheidungloooooooool
-
hmm ich merke gerade, dass ich deque gar nicht von basis ableiten kann, da next ja kein prev kennt... gibt es da eventuell eine möglichkeit, dass next aus der basisklasse prev kennt?
-
uhsuhz schrieb:
hmm ich merke gerade, dass ich deque gar nicht von basis ableiten kann, da next ja kein prev kennt... gibt es da eventuell eine möglichkeit, dass next aus der basisklasse prev kennt?
Ehm.. Du speicherst dir auch einen Verweise auf prev?!

-
@nexus ich habe es nun so gelöst, dass ich in der Containerklasse:
template<class T> class MyContainer { template<class T>friend bool operator==(const MyContainer& leftContainer,const MyContainer& rightContainer); . . . };und dann in der stack klasse noch mal:
template<class T> bool operator==(const MyStack<T>& left,const MyStack<T>& right) { return (static_cast<MyContainer<T>>(left) == static_cast<MyContainer<T>>(left)); } template<class T> bool operator!=(const MyStack<T>& left,const MyStack<T>& right) { return !(left == right); }diese funktionen sind aber nicht freunde der klasse, brauchen sie auch nicht. funktionieren tut es schon mal^^
-
Ableitung macht hier wenig Sinn. Was du willst ist Komposition.
Damit loesen sich uebrigens auch alle Namensprobleme in Luft auf.Stack und Deque sind zwar Container, aber bei Vererbung das Liskov Substitution Principle beachten.
-
Shade Of Mine schrieb:
Ableitung macht hier wenig Sinn. Was du willst ist Komposition.
Damit loesen sich uebrigens auch alle Namensprobleme in Luft auf.Stack und Deque sind zwar Container, aber bei Vererbung das Liskov Substitution Principle beachten.
io ich werde mir das morgen mal anschauen, für heute habe ich genug^^