problem mit stack und basisklasse



  • Also ich habe folgendes Problem:

    Ich habe eine Basisklasse:

    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 *first;
    	unsigned int anz_elem;
    public:
    	LContainer();
    	~LContainer();
    	unsigned int getAnzahlElement();
    	bool is_empty();
    	void clear();
    };
    

    Dies ist die basisklasse von Stack, Queue, und Deque

    nun habe ich aber das problem, dass Stack ja nicht first, sondern top benutzt, nur weiß ich nicht wie ich machen kann, dass ich top und nicht first benutzen kann?



  • Das würde ich nicht mit Referenzen lösen. top() ist normalerweise eine Funktion, genauso wie first() (oder eben front() ) eine Funktion sein sollte, und die das erste Element (und nicht einen Zeiger darauf) zurückliefert. Dann brauchst du in den abgeleiteten Klassen nur die Funktionen jeweils mit dem passenden Namen zu implementieren.

    Trotzdem zu Referenzen: Diese müssen immer im Konstruktor initialisiert werden. Das gilt auch für Referenzen auf Zeiger.



  • das heißt ich soll ich der klasse einfach irgentetwas als bezeichner des ersten elements nehmen, und dann ne funktion, (im Stack: top in der Queue: first), die dann data von dem pointer auf das erste element zurückgibt?



  • 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 entscheidung

    loooooooool



  • 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?! 🙄


Anmelden zum Antworten