# C++ einfach Verkettete Liste - Objekte einfügen



## traknoxx (4. März 2009)

Hallo,

ich bin C++ Schüler und komme bei einer Aufgabe die ich lösen soll nicht weiter.
Vielleicht kann mir hier jemand helfen, wäre für alle Tipps dankbar!

hier zunächst der *Ausgangs-Quellcode:*

/* #################################################
Eine einfach verkettete Liste mit Objekten
################################################# */

#include "stdafx.h"

using namespace System;

ref class listenelement {
	String ^daten;
	listenelement ^next;
public:
	void datenSetzen(String ^datenneu); 
	void einfuegen(String ^datenneu);
	void ausgeben();
};

//die Zeichenkette in das Element schreiben
void listenelement::datenSetzen(String ^datenneu) {
	daten=datenneu;
}

//neues Element am Ende der Liste einfügen
void listenelement::einfuegen(String ^datenneu) {
	//hat next den Wert nullptr?
	//dann ein neues Element einfügen
	if (next == nullptr) {
		next = gcnew listenelement;
		next->daten=datenneu;
		//nullptr wird automatisch zugewiesen!
	}
	//sonst die Methode für next noch einmal aufrufen
	else
		next->einfuegen(datenneu);
	//nur zur Veranschaulichung
	Console::WriteLine("Daten {0} wurden eingefügt.",datenneu);
}

//Alle Elemente der Liste ausgeben
void listenelement::ausgeben() {
	//den ersten Eintrag ausgeben
	Console::WriteLine("{0}",daten);
	//wenn das Ende nicht erreicht ist, die Methode für next erneut aufrufen
	if (next != nullptr)
		next->ausgeben();
}

int main(array<System::String ^> ^args)
{
	//Handle auf den Anfang der Liste
	listenelement ^liste;

	//das erste Element per Hand erzeugen
	liste = gcnew listenelement;
	//Zeichenkette in das erste Element schreiben
	liste->datenSetzen("Element 0");

	//in einer Schleife mehrere Elemente einfügen
	for (Int32 schleife=1;schleife<3;schleife++) 
		liste->einfuegen("Element "+schleife);

	//die Liste ausgeben
	liste->ausgeben();

	return 0;
}


*und hier die Aufgabenstellung:*

Erweitern Sie die einfach verkettete Liste mit den Objekten so, dass das Listenende beim Einfügen nicht immer wieder neu ermittelt werden muss, sondern neue Elemente direkt am Ende der Liste eingefügt werden können.

Hilfestellungen:

-Keine Änderungen an der Klasse selbst
-Sie müssen neben dem Anfang der Liste nun auch das Ende der Liste in einem Handle speichern können.
-Setzten Sie den Handle nach dem Einfügen neuer Elemente jeweils auf das aktuelle Ende der Liste.


*nun mein Lösungsansatz:*


#include "stdafx.h"

using namespace System;

ref class listenelement {
	String ^daten;
	listenelement ^next;
public:
	void datenSetzen(String ^datenneu); 
	void einfuegen(String ^datenneu);
	void ausgeben();
};

//die Zeichenkette in das Element schreiben
void listenelement::datenSetzen(String ^datenneu) {
	daten=datenneu;
}

//neues Element am Ende der Liste einfügen
void listenelement::einfuegen(String ^datenneu) {

*
	//ein neues Element einfügen
	next = gcnew listenelement;

	//Zeichenkette im neuen Element eintragen
	next->daten=datenneu;
	//nullptr wird automatisch zugewiesen!*

	//nur zur Veranschaulichung
	Console::WriteLine("Daten {0} wurden eingefügt.",datenneu);

}

//Alle Elemente der Liste ausgeben
void listenelement::ausgeben() {
	//den ersten Eintrag ausgeben
	Console::WriteLine("{0}",daten);
	//wenn das Ende nicht erreicht ist, die Methode für next erneut aufrufen
	if (next != nullptr)
		next->ausgeben();
}

int main(array<System::String ^> ^args)
{
	//Handle auf den Anfang der Liste
	listenelement ^listenAnfang;

*//Handle auf das Ende der Liste
	listenelement ^listenEnde;*

	//das erste Element per Hand erzeugen
	listenAnfang = gcnew listenelement;
	//Zeichenkette in das erste Element schreiben
	listenAnfang->datenSetzen("Element 0");

*//Default Listenende
	listenEnde = listenAnfang;*

	//in einer Schleife mehrere Elemente einfügen
	for (Int32 schleife=1;schleife<3;schleife++) 
		listenEnde->einfuegen("Element "+schleife);

	//die Liste ausgeben
	listenAnfang->ausgeben();

	return 0;
}

Leider funktioniert das so noch nicht so wie es eingentlich sollte :-(
Ich denke dass das Problem in der Main-Funktion ist, aber ich komme einfach nicht dahinter.

Wäre sehr dankbar für hilfreiche Tipps...

Danke schon mal für eure Mühe

Grüß traknoxx


----------



## 3Cyb3r (4. März 2009)

Postemal alles in COde Tags dann hab ich vielleicht bock mir das anzusehen
[ CODE][/CODE ] Ohne die Leerzeichen. Aber was ich so gesehen habe ist deine Klasse total absurd wenn da das Listenende in der Main setzt mach das doch einfach atomatisch in der EInfüge funktion.


----------



## Matthias Reitinger (4. März 2009)

Man sollte vielleicht auch noch dazu sagen, dass das *kein* C++ ist, sondern C++/CLI.


----------



## traknoxx (5. März 2009)

Danke schon mal für die schnelle Antwort

Ist hoffe das ist so wie du wolltest?


```
/* #################################################

Einsendeaufgabe 6.2

################################################# */

#include "stdafx.h"

using namespace System;

ref class listenelement {
	String ^daten;
	listenelement ^next;
public:
	void datenSetzen(String ^datenneu); 
	void einfuegen(String ^datenneu);
	void ausgeben();
};

//die Zeichenkette in das Element schreiben
void listenelement::datenSetzen(String ^datenneu) {
	daten=datenneu;
}
	
//neues Element am Ende der Liste einfügen
void listenelement::einfuegen(String ^datenneu) {
	
	
	//ein neues Element einfügen
	next = gcnew listenelement;

	//Zeichenkette im neuen Element eintragen
	next->daten=datenneu;
	//nullptr wird automatisch zugewiesen!
	
	//nur zur Veranschaulichung
	Console::WriteLine("Daten {0} wurden eingefügt.",datenneu);
	
}

//Alle Elemente der Liste ausgeben
void listenelement::ausgeben() {
	//den ersten Eintrag ausgeben
	Console::WriteLine("{0}",daten);
	//wenn das Ende nicht erreicht ist, die Methode für next erneut aufrufen
	if (next != nullptr)
		next->ausgeben();
}

int main(array<System::String ^> ^args)
{
	//Handle auf den Anfang der Liste
	listenelement ^listenAnfang;

	//Handle auf das Ende der Liste
	listenelement ^listenEnde;

	//das erste Element per Hand erzeugen
	listenAnfang = gcnew listenelement;
	//Zeichenkette in das erste Element schreiben
	listenAnfang->datenSetzen("Element 0");

	//Default Listenende
	listenEnde = listenAnfang;

	//in einer Schleife mehrere Elemente einfügen
	for (Int32 schleife=1;schleife<3;schleife++) 
		listenEnde->einfuegen("Element "+schleife);

	//die Liste ausgeben
	listenAnfang->ausgeben();

	return 0;
}
```

wenn ich das Listenende in der Einfüge-Funktion setzen kann ohne die Klasse zu verändern würd ich das schon gern machen, komm nur nicht drauf wie?!


----------



## Mizi Mace (5. März 2009)

Einen wunderschönen guten Morgen,



traknoxx hat gesagt.:


> ```
> int main(array<System::String ^> ^args)
> {
> //Handle auf den Anfang der Liste
> ...



Du setzt am Anfang das Listenende auf den Listenanfang, was auch richtig ist. Dort bleibt dieser auch. Deine Listenelemente sollten demnach immer am Anfang eingefügt werden. Du musst das Listenende mit jeden neu eingefügten Knoten auf diesen neuen Knoten setzen, da dies ja dein neues Listenende ist.

EDIT: Eventuell muss die Aktualisierung des Listenendes auch in der Funktion *einfuegen()* geschehen.

Gruss
Mizi


----------



## traknoxx (5. März 2009)

Guten Morge Mizi Mace, 

Danke für die Antwort.



> EDIT: Eventuell muss die Aktualisierung des Listenendes auch in der Funktion einfuegen() geschehen.



Das denke ich auch, aber ich weiß nicht wie ich das machen soll ohne der Funktion das aktuelle Listenende zu übergeben bzw. von der Funktion das neue Listenende zurückgeben zu lassen, dafür müsste ich die Methoden der Klasse ändern, und laut Hilfestellung in der Aufgabenstellung sollte das ja nicht nötig sein...

irgendeinen Tipp wie das noch umsetzbar wäre?   bin echt schon am verzweifeln


----------



## Mizi Mace (5. März 2009)

Einen wunderschönen guten Morgen,

innerhalb einer Klasse steht dir das Element als this-Pointer zur Verfügung. In deinem Beispiel rufst die Methode *einfuegen()* der Klasse _listenelement_ für das Listenelement Listenende auf. Somit kannst du auf das Listenende mit dem this-Pointer zurückgreifen. Du musst dabei aufpassen, dass du die Liste richtig verkettest. Schließlich willst du an das aktuelle Listenende anhängen und erst danach darfst du das Listenende neu setzen.

Gruss
Mizi


----------



## traknoxx (5. März 2009)

Danke das hört sich schon mal gut an.

Das heißt dann das ich das Listenende nicht an die Funktion übergeben muss sondern
über dem  "this-Pointer" darauf zugreifen kann.

also

                     //ein neues Element einfügen
	next = gcnew listenelement;


-------->     hier fehlt dann praktisch das setzen auf das neue Listenende oder?
in etwa sowas---->       listenEnde = listenEnde->next;

 ----->  führt aber immer zur Fehlermeldung "error C2065: 'listenEnde': nichtdeklarierter Bezeichner"?!


	//Zeichenkette im neuen Element eintragen
	next->daten=datenneu;
	//nullptr wird automatisch zugewiesen!

Was mach ich hier denn falsch? oder sehe ich vor lauter Bäume den Wald nicht


----------



## deepthroat (5. März 2009)

Hi.

Das sieht alles etwas merkwürdig aus. Hast du denn das so vorgegeben bekommen?

Wieso hast du keine Datenstruktur für die Liste wo du den Anfang und das Ende der Liste verwalten kannst?

Das die Methode einfuegen heißt ist auch etwas ungünstig, da mit Einfügen eigentlich nicht Anhängen gemeint ist....

Das Problem was du mit der Methode hast, ist das die Variable welche das Ende der Liste speichert nicht aktuell gehalten werden kann.


```
listenEnde->einfuegen("Element "+schleife);
        // --->Hier muss noch das Listenende neu gesetzt werden
```
Hier müßtest du listenEnde auf das neu angefügte Element setzen. Das ist aber nicht möglich da du an das neu anfügte Element hier nicht herankommst.

Gruß


----------



## traknoxx (5. März 2009)

Hi deepthroat, 

Ja der ganz oben aufgeführte *Ausgangs-Quellcode* ist für diese Aufgabe vorgegeben...



> listenEnde->einfuegen("Element "+schleife);
> // --->Hier muss noch das Listenende neu gesetzt werden
> 
> 
> Hier müßtest du listenEnde auf das neu angefügte Element setzen. Das ist aber nicht möglich da du an das neu anfügte Element hier nicht herankommst



Meinst du damit das die Aufgabe so nicht lösbar ist? 

Danke für die Mühe

Gruß


----------



## Mizi Mace (5. März 2009)

Einen wunderschönen guten Tag,

mit Klassen, deren Aufbau und Verwendung hast du dich aber schon beschäftigt. Wenn nicht, empfehle ich dir dies dringend nachzuholen. Auch solltest du dich mit den this-Pointer auseinandersetzen.

Natürlich kennt die Methode *einfuegen()* der Klasse _listenelement_ nicht die Variable _listenEnde_. Diese steht somit dort auch nicht zur Verfügung (demnach der Fehler). Allerdings wird die Methode *einfuegen()* über das Listenelement _listenEnde_ aufgerufen. Das Programm befindet sich somit während des Einfügens neuer Listenelemente im Listenelement _listenEnde_. Und damit man auf die Instanz (hier: Listenelement), in der sich das Programm befindet, selbst zugreifen kann, exisitert der this-Pointer. Dieser wird auch genauso geschrieben:


```
*this
```

Du kannst also mit *this auf die aktuelle Instanz zugreifen. Du kannst also die Zuweisung des Listenendes folgendermaßen vornehmen:


```
*this = next;
```

Das ist vielleicht etwas heikel, gerade die Zuweisung der Instanz zu verändern, mit welcher man gerade arbeitet, aber ich sehe hier keine andere Möglichkeit.

Du erzeugst mit deinen Vorschlag zunächst ein neues Listenelement. Danach setzt du sofort das Listenende auf dieses neue Element (was noch nicht funktioniert). Und danach willst die Daten in das neue Element speichern. Doch wohin werden die Daten wirklich gespeichert? Sicherlich nicht dort, wo es hinsoll. Auch hier der Hinweis von ganz oben: Beschäftige dich mit der Funktionsweise von Klassen.

Verwende bitte wieder Code-Tags. Da lässt sich der Code besser lesen.

Gruss
Mizi


----------



## traknoxx (5. März 2009)

Hey Mizi Mace, 

danke für deine ausführliche Antwort.
Mit Klassen, deren Aufbau und Verwendung mache ich gerade (sozusagen) meine ersten Gehversuche  
Den this-Pointer kannte ich aber noch nicht, finde es daher auch komisch das ich den für die Lösung dieser Aufgabe brauche...

ich habe es jetzt mal so versucht....


```
//neues Element am Ende der Liste einfügen
void listenelement::einfuegen(String ^datenneu) {
	
	
	//ein neues Element einfügen
	next = gcnew listenelement;

	//Listenende auf das neue Element setzen
	*this = next;

	//Zeichenkette im neuen Element eintragen
	next->daten=datenneu;
	}
```

Leider bekomm ich dann den Fehler "error C2582: Die Funktion 'operator =' ist in 'listenelement' nicht verfügbar"

hab ich den this-Pointer nun irgendwie falsch angewendet?

Gruß


----------



## traknoxx (5. März 2009)

noch eine kleine Frage so ganz am Rande...

Wie macht ihr das mit dem "ccp code" einfügen kann keinen Button dafür finden...


----------



## deepthroat (6. März 2009)

Hi.





Mizi Mace hat gesagt.:


> Du kannst also mit *this auf die aktuelle Instanz zugreifen. Du kannst also die Zuweisung des Listenendes folgendermaßen vornehmen:
> 
> 
> ```
> ...


Es ist einfach unsinnig. Dabei würde ja ein Zyklus entstehen.


traknoxx hat gesagt.:


> Den this-Pointer kannte ich aber noch nicht, finde es daher auch komisch das ich den für die Lösung dieser Aufgabe brauche...


Vergiss erstmal den this Zeiger und erkläre mal lieber genau deine Aufgabe!

Sollst du denn überhaupt einen Zeiger auf das Ende der Liste speichern?


traknoxx hat gesagt.:


> noch eine kleine Frage so ganz am Rande...
> 
> Wie macht ihr das mit dem "ccp code" einfügen kann keinen Button dafür finden...


Es gibt keinen Button dafür. Wir schreiben das [code=cpp] ... [/code] immer per Hand. Übrigens kannst du Beiträge auch noch nachträglich bearbeiten!

Gruß


----------



## Mizi Mace (6. März 2009)

Einen wunderschönen guten Morgen,



deepthroat hat gesagt.:


> Es ist einfach unsinnig. Dabei würde ja ein Zyklus entstehen.



Du hast Recht. Das funktioniert nicht. Allerdings erkenne ich keinen Zyklus. Vielmehr wird dadurch der Listenanfang ebenfalls verändert. Eine Zuweisung des this-Pointers auf eine neue Adresse ist nicht möglich. Somit ist das ganze nutzlos.



traknoxx hat gesagt.:


> *und hier die Aufgabenstellung:*
> 
> Erweitern Sie die einfach verkettete Liste mit den Objekten so, dass das Listenende beim Einfügen nicht immer wieder neu ermittelt werden muss, sondern neue Elemente direkt am Ende der Liste eingefügt werden können.
> 
> ...



Die Aufgabenstellung lässt leider nicht sehr viel Freiheitsgrade. Eigentlich bleibt nur die Änderung des Codes in den Methoden. Der richtige Weg wäre, die Klasse um eine Methode für das Einfügen der Elemente am Listenende zu erweitern. Diese würde dann statt dem Rückgabewert void einen Zeiger auf das neue Listenende zurückgeben.

Gruss
Mizi


----------



## traknoxx (6. März 2009)

Guten Morgen, 

ich hab von meinem Lehrer den Tipp bekommen das der Hinweis:



> -Keine Änderungen an der Klasse selbst



nur so zu verstehen ist das keine Attribute und Metohen hinzugefügt werden müssen.
Das heißt Änderungen an den vorhandenen Mothoden sind somit möglich.

Ich hab jetzt folgendes umgebaut:


```
public:
	void datenSetzen(String ^datenneu); 
	listenelement einfuegen(String ^datenneu, listenelement ^listenEnde);
	void ausgeben();
};
```

und 


```
listenelement listenelement::einfuegen(String ^datenneu, listenelement ^listenEnde) {
	
	//Neues Element am Ende der Liste einfügen
	next = gcnew listenelement;

	//Listenende auf das neue Element setzen
	listenEnde = next;

	//Zeichenkette im neuen Element eintragen
	next->daten=datenneu;
	//nullptr wird automatisch zugewiesen!

	//nur zur Veranschaulichung
	Console::WriteLine("Daten {0} wurden eingefügt.",datenneu);
	
	return listenEnde;}
```

und 


```
for (Int32 schleife=1;schleife<3;schleife++) 
	listenEnde =(listenEnde->einfuegen("Element "+schleife, listenEnde));
```


Funktioniert aber leider immer noch nicht :-( 
Jetzt hab ich die Fehler:


> error C2440: 'return': 'listenelement ^' kann nicht in 'listenelement' konvertiert werden
> error C2440: '=': 'listenelement' kann nicht in 'listenelement ^' konvertiert werden



Warum kann nun listenelement nicht in listenelement konvertiert werden? 
das müsste doch eigentlich funktionieren oder?


----------



## deepthroat (6. März 2009)

Mizi Mace hat gesagt.:


> Einen wunderschönen guten Morgen,
> 
> 
> 
> Du hast Recht. Das funktioniert nicht. Allerdings erkenne ich den Zyklus nicht. Dazu müsste die Methode *einfuegen()* rekursiv aufgerufen werden. Ich würde dich bitten, mir den Zyklus zu erklären.


Die Erklärung ist einfach das ich wohl noch nicht wirklich wach war.  Es würde allerdings damit nur das Ende-Element mit einem neuen Element überschrieben werden.

Ich war wohl wirklich noch nicht wach, da ich auch die Aufgabenstellung erstmal gepflegt überlesen habe. 


			
				traknoxx hat gesagt.:
			
		

> Erweitern Sie die einfach verkettete Liste mit den Objekten so, dass das Listenende beim Einfügen nicht immer wieder neu ermittelt werden muss, sondern neue Elemente direkt am Ende der Liste eingefügt werden können.
> 
> Hilfestellungen:
> 
> ...


Die erste Bedingung verhindert eigentlich jede vernünftige Lösung. Wenn die einfuegen Methode aufgerufen wird, passiert irgendetwas mit der Liste, aber man kommt nicht von außen an das Ende der Liste heran. Also kann man auch von außen kein Handle auf das Ende der Liste speichern.

Gruß


----------



## deepthroat (6. März 2009)

traknoxx hat gesagt.:


> Guten Morgen,
> 
> ich hab von meinem Lehrer den Tipp bekommen das der Hinweis:
> 
> nur so zu verstehen ist das keine Attribute und Metohen hinzugefügt werden müssen.


Warum schreibt dein Lehrer das dann nicht? 


traknoxx hat gesagt.:


> Jetzt hab ich die Fehler:
> 
> 
> Warum kann nun listenelement nicht in listenelement konvertiert werden?
> das müsste doch eigentlich funktionieren oder?


Du mußt richtig hinschauen. Da fehlt ein ^.

Gruß


----------



## Mizi Mace (6. März 2009)

Einen wunderschönen guten Morgen,

das macht die ganze Aufgabe um einiges einfacher und sogar lösbar.



traknoxx hat gesagt.:


> ```
> public:
> void datenSetzen(String ^datenneu);
> listenelement einfuegen(String ^datenneu, listenelement ^listenEnde);
> ...



Eine Änderung der Methode *datenSetzen()* ist nicht nötig. Diese dient schließlich nur dazu die Eigenschaften der Instanz zu speichern.



traknoxx hat gesagt.:


> ```
> listenelement listenelement::einfuegen(String ^datenneu, listenelement ^listenEnde) {
> 
> //Neues Element am Ende der Liste einfügen
> ...



Es ist nicht notwendig, dass du das Listenende mit übergibst. Wichtig ist nur, dass du einen Zeiger auf das nach dem Einfügen aktuelle Listenende zurückgibst. Das ist in *next* gespeichert. Außerdem muss der Rückgabetyp ein Zeiger sein (das ist auch die Fehlermeldung).


```
listenelement^ listenelement::einfuegen(String ^datenneu) {
	
	//Neues Element am Ende der Liste einfügen
	next = gcnew listenelement;

	//Zeichenkette im neuen Element eintragen
	next->daten=datenneu;
	//nullptr wird automatisch zugewiesen!

	//nur zur Veranschaulichung
	Console::WriteLine("Daten {0} wurden eingefügt.",datenneu);
	
	return next;
}
```



traknoxx hat gesagt.:


> ```
> for (Int32 schleife=1;schleife<3;schleife++)
> listenEnde =(listenEnde->einfuegen("Element "+schleife, listenEnde));
> ```



Hier musst du lediglich die Rückgabe der Methode (was ja das neue Listenende ist) dem Listenende zuweisen.


```
for (Int32 schleife=1;schleife<3;schleife++) 
	listenEnde =listenEnde->einfuegen("Element "+schleife);
```

Gruss
Mizi


----------



## traknoxx (6. März 2009)

Hallo, 

mein Lehrer meinte dann selbst das es eine "etwas in die Irre führende" Aufgabenstellung sei...   

Vielen Dank für die ausführlichen Antworten! Jetzt funktioniert es  

Hier zum Schluss nochmal komplett...


```
#include "stdafx.h"

using namespace System;

ref class listenelement {
	String ^daten;
	listenelement ^next;
public:
	void datenSetzen(String ^datenneu); 
	listenelement ^einfuegen(String ^datenneu);
	void ausgeben();
};

//die Zeichenkette in das Element schreiben
void listenelement::datenSetzen(String ^datenneu) {
	daten=datenneu;
}
	
//neues Element am Ende der Liste einfügen
listenelement^ listenelement::einfuegen(String ^datenneu) {
	
	//Neues Element am Ende der Liste einfügen    
	next = gcnew listenelement;     
	
	//Zeichenkette im neuen Element eintragen    
	next->daten=datenneu;    
	//nullptr wird automatisch zugewiesen!     
	
	//nur zur Veranschaulichung    
	Console::WriteLine("Daten {0} wurden eingefügt.",datenneu);        
	
	return next;
}
	


//Alle Elemente der Liste ausgeben
void listenelement::ausgeben() {
	//den ersten Eintrag ausgeben
	Console::WriteLine("{0}",daten);
	//wenn das Ende nicht erreicht ist, die Methode für next erneut aufrufen
	if (next != nullptr)
		next->ausgeben();
}

int main(array<System::String ^> ^args)
{
	//Handle auf den Anfang der Liste
	listenelement ^listenAnfang;

	//Handle auf das Ende der Liste
	listenelement ^listenEnde;

	//das erste Element per Hand erzeugen
	listenAnfang = gcnew listenelement;
	//Zeichenkette in das erste Element schreiben
	listenAnfang->datenSetzen("Element 0");

	//Default Listenende
	listenEnde = listenAnfang;

	//in einer Schleife mehrere Elemente einfügen
	for (Int32 schleife=1;schleife<3;schleife++) 
		listenEnde = listenEnde->einfuegen("Element "+schleife);

	//die Liste ausgeben
	listenAnfang->ausgeben();

	return 0;
}
```

Vielen Dank nochmal an die Helfenden!

Gruß


----------

