<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Codereview meiner einfachen generischen Liste]]></title><description><![CDATA[<p>Hi Folks,</p>
<p>da ich eigentlich immer in meinem eigenen Saft brate, würde ich mich freuen, wenn der eine oder die andere <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /> mal einen Kommentar zu der Klasse Liste abgibt...was hätte ich besser machen können, sieht jemand Fehler etc.</p>
<p>Ich habe mir ein Testprogramm gemacht, in dem ich Zeiger auf Klassenobjekte in die Liste packe...ich habe sie aber auch mit anderen Objekten getestet.</p>
<p>Die vorhandenen Methoden sind aufgrund einer Vorgabe enthalten.<br />
Über Kommentare freue ich mich!</p>
<pre><code class="language-cpp">/***************************************************/
/* Autor: Uwe Homm                                 */
/* Datum: 8. Dezember 2011                         */
/* Zweck: Einfach verkettete generische Liste      */
/***************************************************/
#pragma once
#ifndef ___LISTE___
#define ___LISTE___

#include &lt;iostream&gt;		//wegen NULL
using namespace std;

template &lt;class T&gt;
class Liste
{
private:
	class Element
	{
		friend class Liste;
	private:
		Element* next;
		T value;
	};

	unsigned int count;
	Element* head;

public:
	Liste(void){
		head = new Element;
		head-&gt;next = NULL;
		count=0;		
	};

	~Liste(void){

		T rc;
		while ( head-&gt;next != NULL ) {
			rc = remove(1);
		}
		delete (head);

	};

//**********************************************
// add()

	void add(T v){
		Element* n = new Element();

		n-&gt;next = NULL;
		n-&gt;value = v;

		//Fügt zu Beginn der Liste ein
		if ( this-&gt;head-&gt;next != NULL )
		{
			n-&gt;next = this-&gt;head-&gt;next;
		}

		this-&gt;head-&gt;next = n;

		this-&gt;count++;
	};

//**********************************************
// contains()

	bool contains(T v){

		//Hilfspointer
		Element* tmp;
		//Zeiger auf erstes LE bzw. leere Liste
		tmp = head-&gt;next;

		bool rc = false;

		while ( tmp != NULL )
		{
			if ( v == tmp-&gt;value )
			{
				rc = true;
				break;
			}
			tmp = tmp-&gt;next;
		}

		return rc;
	}

//**********************************************
// get()

	T* get(unsigned int pos){

		if ( pos &gt; count || count==0 || pos == 0)
			return NULL;

		//Hilfspointer
		Element* tmp;
		//Zeiger auf erstes LE bzw. leere Liste
		tmp = head-&gt;next;

		int counter = 1;
		T* rc = NULL;

		while ( counter )
		{
			if ( counter == pos )
			{
				rc = &amp;(tmp-&gt;value);
				break;
			}
			counter++;
			tmp = tmp-&gt;next;
		}

		return rc;
	}

//**********************************************
// indexOf()

	int indexOf(T v){

		//Hilfspointer
		Element* tmp;
		//Zeiger auf erstes LE bzw. leere Liste
		tmp = head-&gt;next;

		int counter = 0;
		int rc = -1;

		while ( tmp != NULL )
		{
			counter++;
			if ( v == tmp-&gt;value )
			{
				rc = counter;
				break;
			}
			tmp = tmp-&gt;next;
		}

		return rc;
	}

//**********************************************
// remove()

	T remove(unsigned int pos){

		T rc;

		//1. Fall: Die Position gibt es nicht oder die Liste ist leer
		if ( pos &gt; count || count==0 )
			return rc;

		//2. Fall: Das erste LE entfernen
		if ( pos == 1)
		{
			//Hilfspointer
			Element* tmp;
			//Hilfszeiger für zu löschendes erstes Element
			tmp = head-&gt;next;
			//Listhead zeigt nun auf noch zweites Element
			head-&gt;next = tmp-&gt;next;
			//Daten für Rückgabe sichern
			rc = tmp-&gt;value;
			delete (tmp);
			count--;
			return rc;
		}

		//3. Fall: LE irgendwo in der Mitte
		if ( pos &gt; 1 )
		{
			//Hilfspointer
			Element* tmp;
			//Zeiger auf erstes LE
			tmp = head-&gt;next;

			//Bis zum LE vor dem zu löschenden LE laufen
			for (unsigned int i=2; i&lt;pos; i++)
			{
				tmp = tmp-&gt;next;
			}

			//Daten sichern für Rückgabe
			rc = tmp-&gt;next-&gt;value;

			//Pointer auf zu löschendes Element sichern
			int *ptr = (int*)tmp-&gt;next;

			//Falls letztes LE gelöscht werden soll
			if ( tmp-&gt;next-&gt;next == NULL )
				tmp-&gt;next = NULL;
			else
				//Ansonsten übernachstes LE an aktuelles LE hängen
				tmp-&gt;next = tmp-&gt;next-&gt;next;

			delete(ptr);
			count--;
			return rc;
		}
	};

//**********************************************
// size()

	long size(){
		return this-&gt;count;
	};

//**********************************************
// clearList()

	unsigned int clearList(){

		unsigned int ctr = 0;
		while ( count &gt; 0 )
		{
			remove(1);
			ctr++;
		}

		return ctr;
	}

};
#endif
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/topic/296753/codereview-meiner-einfachen-generischen-liste</link><generator>RSS for Node</generator><lastBuildDate>Fri, 14 Aug 2026 05:42:00 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/296753.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 11 Dec 2011 17:50:18 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Sun, 11 Dec 2011 17:50:18 GMT]]></title><description><![CDATA[<p>Hi Folks,</p>
<p>da ich eigentlich immer in meinem eigenen Saft brate, würde ich mich freuen, wenn der eine oder die andere <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /> mal einen Kommentar zu der Klasse Liste abgibt...was hätte ich besser machen können, sieht jemand Fehler etc.</p>
<p>Ich habe mir ein Testprogramm gemacht, in dem ich Zeiger auf Klassenobjekte in die Liste packe...ich habe sie aber auch mit anderen Objekten getestet.</p>
<p>Die vorhandenen Methoden sind aufgrund einer Vorgabe enthalten.<br />
Über Kommentare freue ich mich!</p>
<pre><code class="language-cpp">/***************************************************/
/* Autor: Uwe Homm                                 */
/* Datum: 8. Dezember 2011                         */
/* Zweck: Einfach verkettete generische Liste      */
/***************************************************/
#pragma once
#ifndef ___LISTE___
#define ___LISTE___

#include &lt;iostream&gt;		//wegen NULL
using namespace std;

template &lt;class T&gt;
class Liste
{
private:
	class Element
	{
		friend class Liste;
	private:
		Element* next;
		T value;
	};

	unsigned int count;
	Element* head;

public:
	Liste(void){
		head = new Element;
		head-&gt;next = NULL;
		count=0;		
	};

	~Liste(void){

		T rc;
		while ( head-&gt;next != NULL ) {
			rc = remove(1);
		}
		delete (head);

	};

//**********************************************
// add()

	void add(T v){
		Element* n = new Element();

		n-&gt;next = NULL;
		n-&gt;value = v;

		//Fügt zu Beginn der Liste ein
		if ( this-&gt;head-&gt;next != NULL )
		{
			n-&gt;next = this-&gt;head-&gt;next;
		}

		this-&gt;head-&gt;next = n;

		this-&gt;count++;
	};

//**********************************************
// contains()

	bool contains(T v){

		//Hilfspointer
		Element* tmp;
		//Zeiger auf erstes LE bzw. leere Liste
		tmp = head-&gt;next;

		bool rc = false;

		while ( tmp != NULL )
		{
			if ( v == tmp-&gt;value )
			{
				rc = true;
				break;
			}
			tmp = tmp-&gt;next;
		}

		return rc;
	}

//**********************************************
// get()

	T* get(unsigned int pos){

		if ( pos &gt; count || count==0 || pos == 0)
			return NULL;

		//Hilfspointer
		Element* tmp;
		//Zeiger auf erstes LE bzw. leere Liste
		tmp = head-&gt;next;

		int counter = 1;
		T* rc = NULL;

		while ( counter )
		{
			if ( counter == pos )
			{
				rc = &amp;(tmp-&gt;value);
				break;
			}
			counter++;
			tmp = tmp-&gt;next;
		}

		return rc;
	}

//**********************************************
// indexOf()

	int indexOf(T v){

		//Hilfspointer
		Element* tmp;
		//Zeiger auf erstes LE bzw. leere Liste
		tmp = head-&gt;next;

		int counter = 0;
		int rc = -1;

		while ( tmp != NULL )
		{
			counter++;
			if ( v == tmp-&gt;value )
			{
				rc = counter;
				break;
			}
			tmp = tmp-&gt;next;
		}

		return rc;
	}

//**********************************************
// remove()

	T remove(unsigned int pos){

		T rc;

		//1. Fall: Die Position gibt es nicht oder die Liste ist leer
		if ( pos &gt; count || count==0 )
			return rc;

		//2. Fall: Das erste LE entfernen
		if ( pos == 1)
		{
			//Hilfspointer
			Element* tmp;
			//Hilfszeiger für zu löschendes erstes Element
			tmp = head-&gt;next;
			//Listhead zeigt nun auf noch zweites Element
			head-&gt;next = tmp-&gt;next;
			//Daten für Rückgabe sichern
			rc = tmp-&gt;value;
			delete (tmp);
			count--;
			return rc;
		}

		//3. Fall: LE irgendwo in der Mitte
		if ( pos &gt; 1 )
		{
			//Hilfspointer
			Element* tmp;
			//Zeiger auf erstes LE
			tmp = head-&gt;next;

			//Bis zum LE vor dem zu löschenden LE laufen
			for (unsigned int i=2; i&lt;pos; i++)
			{
				tmp = tmp-&gt;next;
			}

			//Daten sichern für Rückgabe
			rc = tmp-&gt;next-&gt;value;

			//Pointer auf zu löschendes Element sichern
			int *ptr = (int*)tmp-&gt;next;

			//Falls letztes LE gelöscht werden soll
			if ( tmp-&gt;next-&gt;next == NULL )
				tmp-&gt;next = NULL;
			else
				//Ansonsten übernachstes LE an aktuelles LE hängen
				tmp-&gt;next = tmp-&gt;next-&gt;next;

			delete(ptr);
			count--;
			return rc;
		}
	};

//**********************************************
// size()

	long size(){
		return this-&gt;count;
	};

//**********************************************
// clearList()

	unsigned int clearList(){

		unsigned int ctr = 0;
		while ( count &gt; 0 )
		{
			remove(1);
			ctr++;
		}

		return ctr;
	}

};
#endif
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/2155816</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2155816</guid><dc:creator><![CDATA[uhomm]]></dc:creator><pubDate>Sun, 11 Dec 2011 17:50:18 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Sun, 11 Dec 2011 17:56:09 GMT]]></title><description><![CDATA[<p>#pragma once gehört nicht zum C++ Standard. using namespace xxx; in einem Header ist eine ganz schlechte Idee. NULL ist ein C Makro, dass du aus &lt;cstring&gt; bekommst, nicht aus &lt;iostream&gt;. Sinnvoller wäre aber eh 0 oder gleich nullptr. (Deutsche Variablennamen.. na ja, Geschmackssache..)<br />
Ich les dann mal weiter. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2155818</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2155818</guid><dc:creator><![CDATA[cooky451]]></dc:creator><pubDate>Sun, 11 Dec 2011 17:56:09 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Sun, 11 Dec 2011 17:56:11 GMT]]></title><description><![CDATA[<p>Ne Liste mit Indexzugriff ist ziemlich abnormal</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2155820</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2155820</guid><dc:creator><![CDATA[hmmmmm]]></dc:creator><pubDate>Sun, 11 Dec 2011 17:56:11 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Sun, 11 Dec 2011 18:17:00 GMT]]></title><description><![CDATA[<p>hmmmmm schrieb:</p>
<blockquote>
<p>Ne Liste mit Indexzugriff ist ziemlich abnormal</p>
</blockquote>
<p>Das ist dann keine Liste mehr, sondern ein array/vector :xmas1:</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2155829</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2155829</guid><dc:creator><![CDATA[[[global:guest]]]]></dc:creator><pubDate>Sun, 11 Dec 2011 18:17:00 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Sun, 11 Dec 2011 18:44:41 GMT]]></title><description><![CDATA[<p>Das erste lement sollte den Index 0 haben, wie sonst überall in C auch üblich.</p>
<p>Die leere Liste sollte keinen Knoten haben, sondern head sollte NULL sein.</p>
<p>rc = remove(1); wird zu remove(1); Man muß Rückgaben nicht verarbeiten.</p>
<p>delete (head); wird zu delete head; Die Klammen sind nutzlos und unüblich.</p>
<p>Einige if-Abfragen können wegfallen.</p>
<p>Viele while-Schleifen wären lieber for-Schleifen.</p>
<p>Aus</p>
<pre><code class="language-cpp">while ( tmp != NULL )
        {
            if ( v == tmp-&gt;value )
            {
                rc = true;
                break;
            }
            tmp = tmp-&gt;next;
        }

        return rc;
</code></pre>
<p>wird</p>
<pre><code class="language-cpp">while ( tmp != NULL )
        {
            if ( v == tmp-&gt;value )
            {
                 return true;
            }
            tmp = tmp-&gt;next;
        }

        return false;
</code></pre>
<p>Diesen Trick oft verwenden. Das ist inzwischen nicht nur erlaubt (und widerspricht damit den Dogmen des Strukturierten Programmierens), sondern ist sogar erwünscht (und dann trotzdem noch schauen, ob es als for-Schleife hübsch wäre).</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2155844</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2155844</guid><dc:creator><![CDATA[volkard]]></dc:creator><pubDate>Sun, 11 Dec 2011 18:44:41 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Sun, 11 Dec 2011 18:52:45 GMT]]></title><description><![CDATA[<p>Kann man sich nicht auch eine Iterator-Klasse mit überladenem Inkrement/Dekrement, Zuweisungs und Vergleichsoperatoren (== und &lt;, dann std::rel_ops aus utility einbinden) überladen? Mach ich immer so :xmas1:</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2155848</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2155848</guid><dc:creator><![CDATA[[[global:guest]]]]></dc:creator><pubDate>Sun, 11 Dec 2011 18:52:45 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Sun, 11 Dec 2011 18:58:54 GMT]]></title><description><![CDATA[<p>Hi an alle Rückmelder <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
<p>Nun ja, die Methoden waren eben vorgegeben. Daran konnte ich nichts anders machen. Hinsichtlich der Implementation habe ich Eure Anregungen gelesen und freue mich über die Hinweise.</p>
<p>Grüße<br />
Uwe Homm</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2155849</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2155849</guid><dc:creator><![CDATA[uhomm]]></dc:creator><pubDate>Sun, 11 Dec 2011 18:58:54 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Mon, 12 Dec 2011 10:17:37 GMT]]></title><description><![CDATA[<p>Was mir noch aufgefallen ist und bisher nicht genannt wurde: Namen wie __LISTE__ gehören zu den reservierten Namen, die man nicht benutzen sollte. Am besten man lässt alles bleiben, was zwei aufeinanderfolgende Unterstriche enthält oder mit einem Unterstrich gefolgt von einem Großbuchstaben beginnt.</p>
<p>In echtem Code würde man so'was wie &quot;contains&quot; auch nicht anbieten. Dafür gibt es ja std::find aus &lt;algorithm&gt;. Gut, das erfordert natürlich Iteratoren. Und die würdest Du recht einfach mit Hilfe der Boost-Iterator-Bibliothek bauen können (unter Verwendung von boost::iterator_facade). Als Fingerübung ist das schonmal nicht schlecht. Aber man muss das Rad ja auch nicht neu erfinden; denn es gibt ja schon std::list mit einem schön ausgeklügeltem Interface.</p>
<p>Ich sehe gerade noch remove(int pos). Das ist natürlich großer Käse. Der Parameter sollte hier ein Iterator sein.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156087</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156087</guid><dc:creator><![CDATA[krümelkacker]]></dc:creator><pubDate>Mon, 12 Dec 2011 10:17:37 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Mon, 12 Dec 2011 12:13:19 GMT]]></title><description><![CDATA[<p>#7: __LISTE__ ist ein reservierter Bezeichner<br />
#10: NULL ist in &lt;clocale&gt;, &lt;cstddef&gt;, &lt;cstdlib&gt;, &lt;cstring&gt;, &lt;ctime&gt;, &lt;cwchar&gt; und ggf. &lt;cstdio&gt; zu finden<br />
#19: Da Element ohnehin eine private verschachtelte Klasse ist, scheint mir die Kombination alles private+friend überflüssig zu sein. Ein einfaches struct mit public Zugriff erfüllt den gleichen Zweck. Zweckmäßig wäre aber ggf. ein geeigneter Konstruktor, um das Listenelement gleich initialisieren zu können.<br />
#29: void in Funktionsparameterlisten ist in C++ überflüssig.<br />
#30: Dazu hat volkard schon etwas gesagt. Wenn schon ein dummy-Element benutzt werden soll, ist es i.d.R. empfehlenswert, dieses in das Klassenobjekt direkt einzubetten.<br />
#30: Die Verwendung der Initialisierungsliste ist zu empfehlen.<br />
#38: Hier wird Code dupliziert. Ein Aufruf von clearList ist empfehlenswert.<br />
#44: Ich vermisse einen Copy-Konstruktor und einen Zuweisungsoperator. Ein swap wäre auch nicht schlecht.<br />
#156: Hier wird ggf. der Wert eines nicht initialisierten Objektes zurückgegeben, das erzeugt undefiniertes Verhalten. rc sollte erst definiert werden, wenn es initialisiert werden kann. In dieser Zeile müsste return T(); stehen.</p>
<p>Volkard hat noch ein paar mehr Tipps gegeben.<br />
Hier ein Beispiel, wie ein geeignet geschriebener Konstruktor für Element die Angelegenheit verweinfachen kann:</p>
<pre><code class="language-cpp">struct Element
    {
        Element(const T&amp; v, Element* next = NULL)
            : next(next), value(v)
        {}
        Element* next;
        T value;
    };
[...]
    void add(const T&amp; v){
        head-&gt;next = new Element(v, head-&gt;next);
        ++count;
    };
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/2156126</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156126</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Mon, 12 Dec 2011 12:13:19 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Mon, 12 Dec 2011 19:11:53 GMT]]></title><description><![CDATA[<pre><code class="language-cpp">T remove(unsigned int pos){ 

         //1. Fall: Die Position gibt es nicht oder die Liste ist leer 
         if ( pos &gt; count || count==0 ) 
             return rc; 

         //2. Fall: Das erste LE entfernen 
         if ( pos == 1) 
         { 
             // ...
             return rc; 
         } 

         //3. Fall: LE irgendwo in der Mitte 
         if ( pos &gt; 1 ) 
         { 
             // ...
             return rc; 
         } 
     };
</code></pre>
<p>Das ist Käse.</p>
<p>Erstmal ist der Fall pos == 0 nicht abgedeckt.<br />
Und dann schreibt man exklusiv-oder Bedingungen üblicherweise mit &quot;if-else-if&quot;. Und wenn es eine letzte Bedingung gibt, die &quot;den ganzen Rest&quot; einschliesst, dann schreibt man einfach nur mehr &quot;else&quot;.<br />
Sieht dann so aus:</p>
<pre><code class="language-cpp">T remove(unsigned int pos){ 
         if (pos == 0)
             return T(); // fehler irgendwie behandeln
         else if (pos &gt; count)  // das || count == 0 können wir uns sparen
             return T(); // fehler irgendwie behandeln
         else if (pos == 1) 
         { 
             return WasAuchImmer; 
         } 
         else
         { 
             return WasAuchImmerAnderes; 
         } 
     };
</code></pre>
<p>Und wenn du dann noch bei 0 zu zählen anfängst, wie schon vorgeschlagen wurde, wird die Funktion noch einfacher.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156274</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156274</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Mon, 12 Dec 2011 19:11:53 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Tue, 13 Dec 2011 19:55:22 GMT]]></title><description><![CDATA[<p>hustbaer schrieb:</p>
<blockquote>
<p>Erstmal ist der Fall pos == 0 nicht abgedeckt.</p>
</blockquote>
<p>Wie mich das aufregt!! Es ist viel schöner, wenn du</p>
<pre><code class="language-cpp">if(!pos)
</code></pre>
<p>statt</p>
<pre><code class="language-cpp">if(pos == 0)
</code></pre>
<p>schreibst. :xmas1:</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156656</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156656</guid><dc:creator><![CDATA[[[global:guest]]]]></dc:creator><pubDate>Tue, 13 Dec 2011 19:55:22 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Tue, 13 Dec 2011 22:19:46 GMT]]></title><description><![CDATA[<p>camper schrieb:</p>
<blockquote>
<p>#19: Da Element ohnehin eine private verschachtelte Klasse ist, scheint mir die Kombination alles private+friend überflüssig zu sein. Ein einfaches struct mit public Zugriff erfüllt den gleichen Zweck. Zweckmäßig wäre aber ggf. ein geeigneter Konstruktor, um das Listenelement gleich initialisieren zu können.</p>
</blockquote>
<p>Ich würde ja gleich noch einen Destruktor setzen :xmas1:</p>
<pre><code class="language-cpp">struct Element 
  { 
    // Konstruktor

    ~Element(){
      delete next;
    }

    Element* next; 
    T value; 
  };
</code></pre>
<p>Dann vereinfacht sich der Destruktor der Liste zu:</p>
<pre><code class="language-cpp">~Liste(){
  delete head;
}
</code></pre>
<p>Aber Achtung beim Löschen eines Elementes aus der Liste, nicht das man sich dann die Liste komplett löscht <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156700</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156700</guid><dc:creator><![CDATA[bmario_]]></dc:creator><pubDate>Tue, 13 Dec 2011 22:19:46 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Tue, 13 Dec 2011 23:05:56 GMT]]></title><description><![CDATA[<p>Hacker schrieb:</p>
<blockquote>
<p>hustbaer schrieb:</p>
<blockquote>
<p>Erstmal ist der Fall pos == 0 nicht abgedeckt.</p>
</blockquote>
<p>Wie mich das aufregt!! Es ist viel schöner, wenn du</p>
<pre><code class="language-cpp">if(!pos)
</code></pre>
<p>statt</p>
<pre><code class="language-cpp">if(pos == 0)
</code></pre>
<p>schreibst. :xmas1:</p>
</blockquote>
<p>Ich hoffe das war ein Scherz.<br />
Dinge die ganz Klar &quot;Nummern&quot; sind, prüfe ich *nie* mit &quot;!&quot;.</p>
<p>Wenn es ein int-Cookie ist, wo der Wert Null &quot;nix&quot; bedeutet, dann OK. Wenn es ein Zeiger oder Handle ist OK. bool sowieso. Auch Dinge wie Timer-IDs etc. Alles wo &quot;0&quot; halt heisst &quot;hier ist nix&quot;, und andere Werte keine Bedeutung haben, ausser halt irgendwas zu referenzieren.</p>
<p>Aber nen Index mit &quot;!&quot; zu prüfen. Alter Schwede, wenn ich sowas wo lese geht mir der Hut hoch.<br />
Das ist fast so schlimm wie <code>if (v.size() + 1 &gt; n)</code> und Dergleichen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156707</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156707</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Tue, 13 Dec 2011 23:05:56 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 00:07:07 GMT]]></title><description><![CDATA[<p>Was deine Memberfunktionen angeht, kann ich nur sagen, dass das alles eher nach einem Vector/Array klingt, als nach einer Liste. Die Funktionsnamen klingen auch sehr nach Java, wobei die Liste in Java einfach komplett falsch designt ist.</p>
<p>Ich hab hier mal eine Liste implementiert, du kannst sie ja zur Not selbst erweitern um die gefragten Funktionen, auch, wenn das schwachsinnig ist.</p>
<p>Wir fangen an mit einer Liste, wo alles public ist. Um Kapselung kümmern wir uns später.</p>
<pre><code class="language-cpp">template &lt;typename T&gt; // Ob typename oder class ist grundsätzlich egal, ich bevorzuge typename, weil es meine Intention, einen beliebigen Typen zuzulassen, besser ausdrückt.
struct list
{
	struct node
	{
		node* next;
		T value;
	};

	std::size_t size; // std::size_t ist ein typedef aus &lt;cstddef&gt; und wird gerne für Größenangaben verwendet. size_t ist immer ein unsigned Datentyp, meist unsigned long long int.

	node first;
	node last;
};
</code></pre>
<p>Ich speichere hier 2 Knoten. Der Grund ist, dass ich mich dadurch bei allen Operationen nur noch zwischen zwei Knoten befinde. Damit fallen <strong>alle</strong> Sonderfälle weg, was unseren Code erheblich einfacher und noch dazu flotter macht. Eine leere Liste besteht also aus zwei miteinander verketteten Knoten.</p>
<p>Als Nächstes sollten wir node einen Konstruktor verpassen. Im Konstruktor übergeben wir den Knoten, an dem eingefügt werden soll. Der neue Knoten hängt sich dann selbstständig ein. Außerdem müssen wir noch den Wert des Elements übergeben.</p>
<pre><code class="language-cpp">node(node* prev, T const&amp; value)
	: next(prev-&gt;next)
	, value(value)
{
	prev-&gt;next = this;
}
</code></pre>
<p>Jetzt wirds Zeit, auch der Liste einen Konstruktor zu verpassen. Den Destruktor heben wir uns erstmal für später auf.</p>
<pre><code class="language-cpp">list()
	: size(0)
{
	first.next = &amp;last; // Wir verketten den ersten mit dem letzten Knoten und haben nun eine leere Liste.
	// Manch einer mag hier auf die Idee kommen, ein &quot;last.next = 0&quot; hinzuzufügen. Dies ist jedoch nicht erforderlich, da wird diesen Zeiger nie anrühren!
}
</code></pre>
<p>Leider haben wir jetzt ein neues Problem: Unsere Knoten-Klasse hat keinen Default-Konstruktor. Also schnell einen bauen, er tut einfach gar nichts. Auch hier brauchen wir die Zeiger nicht 0-setzen.</p>
<pre><code class="language-cpp">node()
{}
</code></pre>
<p>Zeit, eine erste Memberfunktion hinzuzufügen. Ich halte mich hier an die STL und bastle als erstes eine push_front Funktion. Sie fügt ein Element am Anfang der Liste ein, also immer genau nach first.</p>
<pre><code class="language-cpp">void push_front(T const&amp; value)
{
	new node(&amp;first, value);
	++size; // Das erhöhen der Größe geschieht nach dem new, damit die Größe auch stimmt, falls new bad_alloc wirft, weil nicht mehr genug Speicher da ist.
}
</code></pre>
<p>Zeit, unsere Liste zu testen! Wir erstellen ein kleines Testprogramm, das die Zahlen von 1 bis 20 einfügt und danach über die Liste iteriert und alle Elemente ausgibt.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;cstddef&gt;

template &lt;typename T&gt;
struct list
{
	struct node
	{
		node(node* prev, T const&amp; value)
			: next(prev-&gt;next)
			, value(value)
		{
			prev-&gt;next = this;
		}

		node()
		{}

		node* next;
		T value;
	};

	list()
		: size(0)
	{
		first.next = &amp;last;
	}

	void push_front(T const&amp; value)
	{
		new node(&amp;first, value);
		++size;
	}

	std::size_t size;

	node first;
	node last;
};

int main()
{
	list&lt;int&gt; l;

	for(int i = 1; i &lt;= 20; ++i)
		l.push_front(i);

	for(list&lt;int&gt;::node* p = l.first.next; p != &amp;l.last; p = p-&gt;next)
		std::cout &lt;&lt; p-&gt;value &lt;&lt; ' ';
}
</code></pre>
<blockquote>
<p>20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1</p>
</blockquote>
<p>Es funktioniert! <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f921.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--clown_face"
      title=":clown:"
      alt="🤡"
    /></p>
<p>Weiter gehts dann in Teil 2 mit pop_front und geeignetem Destruktor. Außerdem werden wir erste Mühen in Kapselung stecken.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156719</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156719</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 14 Dec 2011 00:07:07 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 00:18:09 GMT]]></title><link>https://www.c-plusplus.net/forum/post/2156720</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156720</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Wed, 14 Dec 2011 00:18:09 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 00:34:22 GMT]]></title><description><![CDATA[<p>Machen wir gleich weiter mit pop_front. pop_front löscht das erste Element aus der Liste und verringert damit die Größe der Liste um eins. Nichts leichter als das.</p>
<pre><code class="language-cpp">void pop_front()
{
	node* temp = first.next; // Zu löschenden Knoten zwischenspeichern
	first.next = temp-&gt;next; // Zweiten Knoten als ersten einhängen
	--size; // Diesmal müssen wir die Größe vor dem delete erniedrigen. Das Element wurde ja schon entfernt, bevor delete eine Exception werfen könnte.
	delete temp; // Knoten löschen
}
</code></pre>
<p>Bevor wir uns an einen Destruktor für die Liste wagen, kapseln wir erstmal den size-Member. Die STL bietet uns 2 Funktionen, die mit size in Zusammenhang stehen: .size() und .empty(). Was die Funktionen tun, sollte selbsterklärend sein. Das Problem mit .size() ist, dass wir unseren Member bereits size genannt haben. Ich versuche mir gerade die Konvention anzugewöhnen, allen Membervariablen einen _ nachzustellen. Wir machen das hier nun auch einfach so und benennen size in size_ um, alle anderen Membervariablen natürlich auch so.</p>
<pre><code class="language-cpp">bool empty() const // Die Funktion verändert das Objekt nicht, also markieren wir sie als const
{
	return size_ == 0;
}

std::size_t size() const // Auch hier const nicht vergessen
{
	return size_;
}
</code></pre>
<p>Damit könnten wir schon einen Destruktor implementieren, allerdings implementiere ich zuerst .clear(), welche wir dann einfach im Destruktor aufrufen können. clear leert, wie der Name schon sagt, die komplette Liste.</p>
<pre><code class="language-cpp">void clear()
{
	// Wenn wir das so machen, ist es langsamer, als einfach alle Knoten direkt zu löschen, da pop_front noch Zeiger umhängt. Ich vernachlässige diesen Overhead fürs erste.
	while(!empty())
		pop_front();
}
</code></pre>
<p>Nun ist der Destruktor der Liste auch nicht mehr schwer:</p>
<pre><code class="language-cpp">~list()
{
	clear();
}
</code></pre>
<p>In Teil 3 gehts dann weiter mit Iteratoren, damit wir auch first und last private machen können und ein STL-Interface haben.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156721</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156721</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 14 Dec 2011 00:34:22 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 02:03:54 GMT]]></title><description><![CDATA[<p>Was bringt uns der beste Container, wenn wir uns als Benutzer mit Internas wie einer Knoten-Klasse herumschlagen müssen? Jetzt ist es an der Zeit, Iteratoren für unsere Liste zu implementieren. Ein Iterator ist einem Zeiger sehr ähnlich: Er lässt sich mit ++ erhöhen und mit * kommen wir an das Element. Der -&gt; Operator funktioniert ebenfalls ähnlich einem Zeiger. Mehr zu Iteratoren findest du <a href="http://magazin.c-plusplus.net/artikel/Aufbau%20der%20STL%20-%20Teil%202%3A%20Iteratoren%20und%20Algorithmen" rel="nofollow">hier</a>.</p>
<p>Die Iterator-Klasse stecken wir auch einfach in die Listen-Klasse hinein.</p>
<pre><code class="language-cpp">struct iterator
{
	iterator(node* pos)
		: pos_(pos)
	{}

	iterator&amp; operator ++ () // Präfix-Inkrement
	{
		pos_ = pos_-&gt;next_; // Wenn der User ++ schreibt, gehen wir einfach zum nächsten Knoten.
		return *this;
	}

	iterator operator ++ (int) // Postfix-Inkrement
	{
		iterator temp(*this);
		++*this;
		return temp;
	}

	T&amp; operator * () // non-const Version
	{
		return pos_-&gt;value_;
	}

	T const&amp; operator * () const // const-Version
	{
		return pos_-&gt;value_;
	}

	T* operator -&gt; () // Auch hier wieder eine non-const Version...
	{
		return &amp;*this;
	}

	T const* operator -&gt; () const // ... und eine const-Version
	{
		return &amp;*this;
	}

	friend bool operator == (iterator const&amp; first, iterator const&amp; second)
	{
		return first.pos_ == second.pos_;
	}

	// Eigentlich müsste man den Kollegen gar nicht zum Freund erklären, aber damit man ihn ins selbe Arbeitszimmer setzen kann, machen wir eine Ausnahme.
	friend bool operator != (iterator const&amp; first, iterator const&amp; second)
	{
		return !(first == second);
	}

private:
	node* pos_; // Ein Iterator speichert sich den Knoten, an dem er sich gerade befindet.
};
</code></pre>
<p>Falls du hier mit fragendem Gesicht vor dem Bildschirm sitzt: In <a href="http://magazin.c-plusplus.net/artikel/%DCberladung%20von%20Operatoren%20in%20CPlusPlus%20%28Teil%201%29" rel="nofollow">diesem</a> Artikel wird auf die Überladung von Operatoren eingegangen.</p>
<p>Damit der User sich nun auch Iteratoren holen kann, müssen wir ihm die Funktionen begin() und end() zur Verfügung stellen. begin() gibt einen Iterator auf das erste Element (also das Element nach first_) zurück, end() einen Iterator <strong>eins hinter das letzte Element</strong>, also auf last_.</p>
<pre><code class="language-cpp">iterator begin()
{
	return iterator(first_.next_);
}

iterator end()
{
	return iterator(&amp;last_);
}
</code></pre>
<p>Damit können wir first_ und last_ private machen und die Liste wieder einmal testen. Wir nehmen den Code von letztem mal her und ändern die Ausgabeschleife wie folgt ab:</p>
<pre><code class="language-cpp">for(list&lt;int&gt;::iterator iter = l.begin(); iter != l.end(); ++iter)
	std::cout &lt;&lt; *iter &lt;&lt; ' ';
</code></pre>
<p>Wir können jetzt sogar schon einige STL-Algorithmen mit unserer Liste verwenden. Wie wäre es mit dem Durchschnitt aller Werte?</p>
<pre><code class="language-cpp">std::cout &lt;&lt; std::accumulate(l.begin(), l.end(), 0) / static_cast&lt;double&gt;(l.size());
</code></pre>
<p>std::accumulate aus dem Header &lt;numeric&gt; addiert alle Werte, danach teilen wir durch die Anzahl.</p>
<p>Unser Code dürfte nun in etwa so aussehen:</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;numeric&gt;
#include &lt;cstddef&gt;

template &lt;typename T&gt;
struct list
{
	struct node
	{
		node(node* prev, T const&amp; value)
			: next_(prev-&gt;next_)
			, value_(value)
		{
			prev-&gt;next_ = this;
		}

		node()
		{}

		node* next_;
		T value_;
	};

	struct iterator
	{
		iterator(node* pos)
			: pos_(pos)
		{}

		iterator&amp; operator ++ ()
		{
			pos_ = pos_-&gt;next_;
			return *this;
		}

		iterator operator ++ (int)
		{
			iterator temp(*this);
			++*this;
			return temp;
		}

		T&amp; operator * ()
		{
			return pos_-&gt;value_;
		}

		T const&amp; operator * () const
		{
			return pos_-&gt;value_;
		}

		T* operator -&gt; ()
		{
			return &amp;*this;
		}

		T const* operator -&gt; () const
		{
			return &amp;*this;
		}

		friend bool operator == (iterator const&amp; first, iterator const&amp; second)
		{
			return first.pos_ == second.pos_;
		}

		friend bool operator != (iterator const&amp; first, iterator const&amp; second)
		{
			return !(first == second);
		}

	private:
		node* pos_;
	};

	list()
		: size_(0)
	{
		first_.next_ = &amp;last_;
	}

	~list()
	{
		clear();
	}

	void push_front(T const&amp; value)
	{
		new node(&amp;first_, value);
		++size_;
	}

	void pop_front()
	{
		node* temp = first_.next_;
		first_.next_ = temp-&gt;next_;
		--size_;
		delete temp;
	}

	bool empty() const
	{
		return size_ == 0;
	}

	std::size_t size() const
	{
		return size_;
	}

	void clear()
	{
		while(!empty())
			pop_front();
	}

	iterator begin()
	{
		return iterator(first_.next_);
	}

	iterator end()
	{
		return iterator(&amp;last_);
	}

private:
	std::size_t size_;

	node first_;
	node last_;
};

int main()
{
	list&lt;int&gt; l;

	for(int i = 1; i &lt;= 20; ++i)
		l.push_front(i);

	std::cout &lt;&lt; std::accumulate(l.begin(), l.end(), 0) / static_cast&lt;double&gt;(l.size());
}
</code></pre>
<p>In Teil 4 werden wir uns um etwas kümmern, das wir in unserem Eifer ganz vergessen haben: Die großen Drei!<br />
Allerdings ist es schon spät, daher folgt Teil 4 möglicherweise erst Morgen <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156722</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156722</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 14 Dec 2011 02:03:54 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 01:41:02 GMT]]></title><description><![CDATA[<p>Wozu genau brauchst du den last-Node? Wenn der unitialisiert bleibt, und du lediglich &amp;last benutzt, um das Ende zu bestimmen, hättest du die Liste genauso gut mit 0 terminieren können.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156724</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156724</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Wed, 14 Dec 2011 01:41:02 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 01:53:19 GMT]]></title><description><![CDATA[<p>Jetzt wo du es sagst - Du hast Recht, der last-Knoten ist natürlich unnötig. Gewohnheit von doppelt verketteten Listen <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f921.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--clown_face"
      title=":clown:"
      alt="🤡"
    /><br />
Wenn ich jetzt aber alles umbaue, ist die Gefahr, dass sich ein Fehler einschleicht, ziemlich hoch, daher lasse ich es nun so.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156725</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156725</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 14 Dec 2011 01:53:19 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 03:14:11 GMT]]></title><description><![CDATA[<p>camper schrieb:</p>
<blockquote>
<p>Wozu genau brauchst du den last-Node?</p>
</blockquote>
<p>Ich denke nen Sentinel verwenden ist gar nicht unüblich bei verketteten Listen.<br />
Macht die Implementierung von end() und operator ++ einfacher.</p>
<blockquote>
<p>In Teil 4 werden wir uns um etwas kümmern ...</p>
</blockquote>
<p>Those who can...</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156727</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156727</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Wed, 14 Dec 2011 03:14:11 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 08:11:48 GMT]]></title><description><![CDATA[<p>Der Kopierkonstruktor und Zuweisungsoperator fehlen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156748</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156748</guid><dc:creator><![CDATA[DocShoe]]></dc:creator><pubDate>Wed, 14 Dec 2011 08:11:48 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 08:27:04 GMT]]></title><description><![CDATA[<p>Ich sehe keinen Vorteil mit dem first Node direkt in der Liste. Nur weil du keine vernünftige Fehlerabfrage machst, heißt das nicht, dass sie nicht nötig wäre. Probiere etwa mal auf einer leeren Listen ein pop_front(). Dann zerschießt es dir die ganze Liste. Und pop_front() ist die einzigste Funktion, die tatsächlich eine Fallunterscheidung braucht.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156756</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156756</guid><dc:creator><![CDATA[bmario_]]></dc:creator><pubDate>Wed, 14 Dec 2011 08:27:04 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 09:15:58 GMT]]></title><description><![CDATA[<p>DocShoe schrieb:</p>
<blockquote>
<p>Der Kopierkonstruktor und Zuweisungsoperator fehlen.</p>
</blockquote>
<p>--&gt;</p>
<p>314159265358979 schrieb:</p>
<blockquote>
<p>In Teil 4 werden wir uns um etwas kümmern, das wir in unserem Eifer ganz vergessen haben: Die großen Drei!</p>
</blockquote>
<p>bmario_ schrieb:</p>
<blockquote>
<p>Nur weil du keine vernünftige Fehlerabfrage machst, heißt das nicht, dass sie nicht nötig wäre. Probiere etwa mal auf einer leeren Listen ein pop_front(). Dann zerschießt es dir die ganze Liste. Und pop_front() ist die einzigste Funktion, die tatsächlich eine Fallunterscheidung braucht.</p>
</blockquote>
<p>--&gt;</p>
<p>n3290 schrieb:</p>
<blockquote>
<p>void pop_front();<br />
Effects: erase_after(before_begin())</p>
</blockquote>
<p>--&gt;</p>
<p>n3290 schrieb:</p>
<blockquote>
<p>iterator erase_after(const_iterator position);<br />
Requires: The iterator following position is dereferenceable. Effects: Erases the element pointed to by the iterator following position.<br />
Returns: An iterator pointing to the element following the one that was erased, or end() if no such element exists.<br />
Throws: Nothing.</p>
</blockquote>
<p>Und damit ist der Benutzer dann selbst schuld, wenn er auf einer leeren Liste pop_front() aufruft. Man könnte natürlich asserts einbauen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156774</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156774</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 14 Dec 2011 09:15:58 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 12:54:51 GMT]]></title><description><![CDATA[<p>Nachdem wir nun Iteratoren implementiert haben, haben wir schon einen Großteil geschafft. Allerdings tun die vom Compiler generierten Kopier-Operationen nicht das richtige. Die <a href="http://de.wikipedia.org/wiki/Dreierregel_%28C%2B%2B%29" rel="nofollow">Regel der großen Drei</a> besagt, dass wir den Kopierkonstruktor und Kopier-Zuweisungsoperator implementieren müssen, da wir einen Destruktor implementiert haben.</p>
<p>Fangen wir mit dem Kopierkonstruktor an. Wir kopieren einfach jedes Element der Liste. Damit wir an den pos-Member des Iterators herankommen, erklären wir list zusätzlich als friend der Klasse iterator.</p>
<pre><code class="language-cpp">list(list const&amp; other)
	: size_(other.size_)
{
	first_.next = 0;

	for(iterator iter = other.begin(), curr = iterator(first_); iter != other.end(); ++iter, ++curr)
		new node(curr.pos, *iter);
}
</code></pre>
<p>Jetzt fehlt nur noch der Zuweisungsoperator. Am einfachsten ist es, diesen über das &quot;Copy &amp; Swap&quot; - Idiom zu implementieren. Siehe auch: <a href="http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap" rel="nofollow">http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap</a></p>
<p>Dafür brauchen wir eine Funktion, die *this mit einer andere Liste tauscht. Am einfachsten geht das, indem wir alle Knoten zwischen first und last der beiden Listen tauschen. Doch hier haben wir ein Problem: Das Tauschen lässt sich nicht effizient implementieren, da wird einen last-Knoten haben. Wir müssten die Liste bis zum letzten Element vor last durchgehen, um den Zeiger des Knotens auf das neue last setzen zu können. Wir kommen zu dem Entschluss, dass wir auf camper hätten hören sollen und bauen unsere Liste so um, dass das Ende durch einen next-Zeiger mit dem Wert 0 repräsentiert wird.</p>
<p>Dazu sind lediglich ein paar Änderungen erforderlich. Als erstes entfernen wir den last-Knoten. Nun müssen wir nur alle Stellen korrigieren, an denen wir last verwendet haben. Als erstes wäre da der Konstruktor. Wir haben first_.next_ mit &amp;last initialisiert, das ersetzen wir durch 0:</p>
<pre><code class="language-cpp">list()
	: size_(0)
{
	first_.next_ = 0;
}
</code></pre>
<p>Die zweite Stelle, an der wir last verwendet haben, ist die end()-Methode. Anstatt eines Iterators auf &amp;last geben wir einen Iterator auf 0 zurück:</p>
<pre><code class="language-cpp">iterator end()
{
	return iterator(0);
}
</code></pre>
<p>War also doch nicht so schlimm, wie ursprünglich angenommen. Nun können wir unser swap() implementieren. Wir tauschen einfach die Knoten der Listen sowie die Größen:</p>
<pre><code class="language-cpp">void swap(list&amp; other)
{
	swap(first_.next_, other.first_.next_);
	swap(size_, other.size_);
}
</code></pre>
<p>Damit wird der Zuweisungsoperator auch einfach:</p>
<pre><code class="language-cpp">list&amp; operator = (list other)
{
	swap(other);
	return *this;
}
</code></pre>
<p>Der Vollständigkeit halber sollten wir noch eine freie Funktion zum Tauschen zweier Listen implementieren:</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void swap(list&lt;T&gt;&amp; first, list&lt;T&gt;&amp; second)
{
	first.swap(second);
}
</code></pre>
<p>In Teil 5 werden wir noch ein paar Konstruktoren und Methoden sowie ein paar typedefs hinzufügen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156781</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156781</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 14 Dec 2011 12:54:51 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 17:55:35 GMT]]></title><description><![CDATA[<p>Vorhin schrieb ich, dass schon die meisten Standard-Algortihmen mit unserer Liste funktionieren. Manche Algorithmen setzen jedoch voraus, dass sowohl die Liste, als auch unsere Iterator-Klasse bestimmte Typen definiert.</p>
<p>Die folgenden typedefs können wir in die Liste stecken.</p>
<pre><code class="language-cpp">typedef T value_type;

typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;

typedef T&amp; reference;
typedef T const&amp; const_reference;

typedef T* pointer;
typedef T const* const_pointer;
</code></pre>
<p>Für die typedefs im Iterator gibt es glücklicherweise eine Abkürzung in der STL. Mithilfe der Klasse iterator aus &lt;iterator&gt; können wir uns die Tipparbeit sparen. Dazu müssen wir unseren iterator nur von std::iterator ableiten.</p>
<pre><code class="language-cpp">struct iterator : std::iterator&lt;std::forward_iterator_tag, T&gt;
</code></pre>
<p>Das forward_iterator_tag gibt den Typen des Iterators an. Da wir eine einfach verkettete Liste haben, haben wir nur forward access.</p>
<p>Da wir nun die ganzen typedefs haben, wäre es besser, überall im Code T durch das entsprechende typedef zu ersetzen.</p>
<p>Damit kommen wir zu folgendem Code:</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;iterator&gt;
#include &lt;numeric&gt;
#include &lt;cstddef&gt;

template &lt;typename T&gt;
struct list
{
	typedef T value_type;

	typedef std::size_t size_type;
	typedef std::ptrdiff_t difference_type;

	typedef T&amp; reference;
	typedef T const&amp; const_reference;

	typedef T* pointer;
	typedef T const* const_pointer;

	struct node
	{
		node(node* prev, const_reference value)
			: next_(prev-&gt;next_)
			, value_(value)
		{
			prev-&gt;next_ = this;
		}

		node()
		{}

		node* next_;
		value_type value_;
	};

	struct iterator : std::iterator&lt;std::forward_iterator_tag, value_type&gt;
	{
		iterator(node* pos)
			: pos_(pos)
		{}

		iterator&amp; operator ++ ()
		{
			pos_ = pos_-&gt;next_;
			return *this;
		}

		iterator operator ++ (int)
		{
			iterator temp(*this);
			++*this;
			return temp;
		}

		reference operator * ()
		{
			return pos_-&gt;value_;
		}

		const_reference operator * () const
		{
			return pos_-&gt;value_;
		}

		pointer operator -&gt; ()
		{
			return &amp;*this;
		}

		const_pointer operator -&gt; () const
		{
			return &amp;*this;
		}

		friend bool operator == (iterator const&amp; first, iterator const&amp; second)
		{
			return first.pos_ == second.pos_;
		}

		friend bool operator != (iterator const&amp; first, iterator const&amp; second)
		{
			return !(first == second);
		}

	private:
		node* pos_;
	};

	list()
		: size_(0)
	{
		first_.next_ = 0;
	}

	list(list const&amp; other)
		: size_(other.size_)
	{
		first_.next_ = 0;

		for(iterator iter = other.begin(), curr = iterator(first_); iter != other.end(); ++iter, ++curr)
			new node(curr.pos, *iter);
	}

	list&amp; operator = (list other)
	{
		swap(other);
		return *this;
	}

	~list()
	{
		clear();
	}

	void swap(list&amp; other)
	{
		swap(first_.next_, other.first_.next_);
		swap(size_, other.size_);
	}

	void push_front(const_reference value)
	{
		new node(&amp;first_, value);
		++size_;
	}

	void pop_front()
	{
		node* temp = first_.next_;
		first_.next_ = temp-&gt;next_;
		--size_;
		delete temp;
	}

	bool empty() const
	{
		return size_ == 0;
	}

	size_type size() const
	{
		return size_;
	}

	void clear()
	{
		while(!empty())
			pop_front();
	}

	iterator begin()
	{
		return iterator(first_.next_);
	}

	iterator end()
	{
		return iterator(0);
	}

private:
	std::size_t size_;

	node first_;
};

template &lt;typename T&gt;
void swap(list&lt;T&gt;&amp; first, list&lt;T&gt;&amp; second)
{
	first.swap(second);
}

int main()
{
	list&lt;int&gt; l;

	for(int i = 1; i &lt;= 20; ++i)
		l.push_front(i);

	std::cout &lt;&lt; std::accumulate(l.begin(), l.end(), 0) / static_cast&lt;double&gt;(l.size());
}
</code></pre>
<p>Leider habe ich gerade keine Zeit dafür, was ich noch schreiben wollte. Das folgt dann in Teil 6.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156840</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156840</guid><dc:creator><![CDATA[314159265358979]]></dc:creator><pubDate>Wed, 14 Dec 2011 17:55:35 GMT</pubDate></item><item><title><![CDATA[Reply to Codereview meiner einfachen generischen Liste on Wed, 14 Dec 2011 18:07:29 GMT]]></title><description><![CDATA[<p>314159265358979 schrieb:</p>
<blockquote>
<p>Dafür brauchen wir eine Funktion, die *this mit einer andere Liste tauscht. Am einfachsten geht das, indem wir alle Knoten zwischen first und last der beiden Listen tauschen. Doch hier haben wir ein Problem: Das Tauschen lässt sich nicht effizient implementieren, da wird einen last-Knoten haben. Wir müssten die Liste bis zum letzten Element vor last durchgehen, um den Zeiger des Knotens auf das neue last setzen zu können. Wir kommen zu dem Entschluss, dass wir auf camper hätten hören sollen und bauen unsere Liste so um, dass das Ende durch einen next-Zeiger mit dem Wert 0 repräsentiert wird.</p>
</blockquote>
<p>An die Implementierung hatte ich gar nicht gedacht, 0 hat noch einen anderen Vorteil: der end-Iterator (und damit jede beliebe Sequenz, wenn wir mal den vor-beginn-Iterator ignorieren) bleibt gültig trotz des swap. Damit sind die end-Iteratoren aller Listen identisch, eine Eigenschaft, die mit anderen Forward-Strukturen geteilt wird (man denke an streams).</p>
<p>Nebenbei führt die Konstanz eines Iterators nicht dazu, dass sich der Typ der Referenz ändert. Wenn du const_iteratoren implementieren willst, wirst du einen eigenständigen Typ benötigen. Oft kann man const_iterator bauen und dann iterator davon ableiten und die entsprechenden Operatoren (mit ein paar unproblematischen const_casts) über die Basisklasse implementieren. Damit ist dann auch gleich die Konvertierung möglich.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2156845</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2156845</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Wed, 14 Dec 2011 18:07:29 GMT</pubDate></item></channel></rss>