Problem mit vordefinierten Parametern/Attributen



  • Hi,

    ich bin Informatik-Student und hab bisher mehr Erfahrung in Java.
    Jetzt belege ich aber gerade das Wahlpflichtfach "C++" und hab ein kleineres(?) Problem.

    Als Übung sollen wir eine doppelt-verkettete Liste schreiben.
    Das funktioniert auch schon soweit, nur mit den vorbesetzten Parametern motzt mein Compiler...

    Hier mal ein paar Code-Auszüge:

    DoubleLinkedList.h:

    #ifndef DOUBLELINKEDLIST_H_
    #define DOUBLELINKEDLIST_H_
    
    #include "DoubleLinkedListElement.h"
    #include <iostream>
    
    using namespace std;
    
    class DoubleLinkedList
    {	
    	friend class DoubleLinkedListElement;
    	friend DoubleLinkedList operator+(const DoubleLinkedList&, const DoubleLinkedList&);
    
    	private:
    		unsigned int length;
    		DoubleLinkedListElement* head;
    		void init();
    	public:	
    		DoubleLinkedList();
    		DoubleLinkedList(const char** , unsigned int );
    		.
    		.
    		.
    }
    

    DoubleLinkedList.cpp:

    #include "DoubleLinkedList.h"
    #include <cstring>
    
    DoubleLinkedList::DoubleLinkedList()
    {
    	init();
    }	
    
    DoubleLinkedList::DoubleLinkedList(const char* elements[], unsigned int count, unsigned int offset=10)
    {
    	init();
    	addArray(elements,count,offset);
    }
    
    .
    .
    .
    

    Makefile:

    DEP     = Test.cpp DoubleLinkedList.cpp DoubleLinkedListElement.cpp DoubleLinkedList.h DoubleLinkedListElement.h
    CC      = /usr/bin/g++
    CFLAGS  = -Wall
    BIN     = DoubleLinkedList
    OBJ		= Test.o DoubleLinkedList.o DoubleLinkedListElement.o
    
    all: $(OBJ)
    	$(CC) $(CFLAGS) -o $(BIN) $(OBJ)
    
    $(BIN): $(DEP)
    	$(CC) $(CFLAGS) -o $(BIN) $(OBJ)
    
    %.o: %.cpp
    	$(CC) $(CFLAGS) -c $<
    
    clean:
    	rm -rf $(BIN) $(OBJ)
    

    Test.cpp:

    #include <iostream>
    #include <cstdlib>
    using namespace std;
    #include "DoubleLinkedList.h"
    
    /**
     * Startfunktion des Programms
     */
    int main(int argc, char **argv) {
    
    	const char* a[] = {"AAA","BBB","CCC","DDD","EEE","FFF","GGG","HHH","III","JJJ"};
    	DoubleLinkedList dll(a,10);
    	dll.print();
    
    }
    

    Ausgabe beim Kompilieren:

    make -k all 
    /usr/bin/g++ -Wall -c Test.cpp
    /usr/bin/g++ -Wall -c DoubleLinkedList.cpp
    DoubleLinkedList.cpp:15: Fehler: Prototyp für »DoubleLinkedList::DoubleLinkedList(const char**, unsigned int, unsigned int)« passt zu nichts in Klasse »DoubleLinkedList«
    DoubleLinkedList.h:10: Fehler: Kandidaten sind: DoubleLinkedList::DoubleLinkedList(const DoubleLinkedList&)
    DoubleLinkedList.h:20: Fehler:                 DoubleLinkedList::DoubleLinkedList(const char**, unsigned int)
    DoubleLinkedList.cpp:4: Fehler:                 DoubleLinkedList::DoubleLinkedList()
    /usr/bin/g++ -Wall -c DoubleLinkedListElement.cpp
    make: *** [DoubleLinkedList.o] Fehler 1
    make: Das Target »all« wurde wegen Fehlern nicht aktualisiert.
    

    Der Fehler tritt auch noch bei einer weiteren Funktion auf, aber ich geh mal davon aus, dass das die selbe Ursache hat.

    Zum Programmieren benutze ich Eclipse mit CDT.
    Mein OS ist Ubuntu 8.10 und mein Compiler g++ 4.3.2

    Falls mir jemand bei meinem Denkfehler helfen kann wär das echt klasse.

    Danke schon mal im voraus 🙂

    P.S.: Die Programmiersprache ist C++, die C#-Tags sind nur zur Übersichtlichkeit.



  • du hast in dem einen konstruktor in der header datei 2 parameter angegeben aber im cpp file dann 3...

    das ist der einzige fehler



  • Naja, es ist genau das aufgetreten, was die Fehlermeldung auch beschreibt. Du hast in der .cpp-Datei eine Memberfunktion definiert, deren Signatur + Name so nicht in der Deklaration (im Header) auftritt.

    MfG,
    Michael

    PS: Ich schwöre, die Antwort war da vorher noch nicht! Das ist Voodoo!



  • Ups,

    ich hatte zwischenzeitlich ne Lösung mit 2 Parametern eingebaut und jetzt wieder falsch rausgenommen.

    Also richtig soll es so sein:

    DoubleLinkedList.h:

    .
    .
    .
    DoubleLinkedList(const char** , unsigned int ,unsigned int );
    .
    .
    .
    

    und jetzt die Ausgabe vom Compiler:

    make -k all 
    /usr/bin/g++ -Wall -c DoubleLinkedList.cpp
    /usr/bin/g++ -Wall -o DoubleLinkedList Test.o DoubleLinkedList.o DoubleLinkedListElement.o
    Test.o: In function `main':
    Test.cpp:(.text+0xc9): undefined reference to `DoubleLinkedList::DoubleLinkedList(char const**, unsigned int)'
    collect2: ld gab 1 als Ende-Status zurück
    make: *** [all] Fehler 1
    

    Nur, warum klappt das nicht?



  • Ariol schrieb:

    P.S.: Die Programmiersprache ist C++, die C#-Tags sind nur zur Übersichtlichkeit.

    Es gibt auch C++-Tags (erste Schaltfläche). 😉



  • Das ist sozusagen das Gegenproblem zu dem letzten. Jetzt fehlt die Implementierung einer Funktion, die im Header aber deklariert wurde. (Und für einen Aufruf in deiner main.cpp den besten "Match" für den Überladenen Konstruktor liefern würde)

    PS: Ich sehe, du kommst wahrscheinlich mit default-parametern noch nicht so zurecht. Wenn du eine Memberfunktion definierst, dann bedeutet dort die Angabe eines Standardparameters nicht, dass die Funktion für den Compiler dann diejenige in der Deklaration ist, die halt diesen Parameter nicht bietet. Diese Defaultwerte sind nur dazu da, dass ein Aufrufer bei oft auftretenden Fällen nicht immer dieselben Parameter angeben muss (Darum müssen die auch eher im Header definiert sein...)



  • Wurde sie ja eben nicht.

    Hier mal alles:

    DoubleLinkedList.h

    #ifndef DOUBLELINKEDLIST_H_
    #define DOUBLELINKEDLIST_H_
    
    #include "DoubleLinkedListElement.h"
    #include <iostream>
    
    using namespace std;
    
    class DoubleLinkedList
    {	
    	friend class DoubleLinkedListElement;
    	friend DoubleLinkedList operator+(const DoubleLinkedList&, const DoubleLinkedList&);
    
    	private:
    		unsigned int length;
    		DoubleLinkedListElement* head;
    		void init();
    	public:	
    		DoubleLinkedList();
    		DoubleLinkedList(const char** , unsigned int ,unsigned int );
    		//DoubleLinkedList(const char**, unsigned int);
    		void push_back(const char*);
    		void push_front(const char*);
    		const char* pop_back();
    		const char* pop_front();
    		void insert(unsigned int,const char*);
    		void addArray(const char*[] ,unsigned int ,unsigned int);
    		void remove(unsigned int);
    		unsigned int getLength() const;
    		bool operator==(const DoubleLinkedList&) const;
    		bool operator!=(const DoubleLinkedList&) const;
    		DoubleLinkedList operator+=(const DoubleLinkedList&);
    		const char* operator[](unsigned int) const;
    		void print();
    };
    
    //#include "DoubleLinkedList.cpp"
    
    #endif /*DOUBLELINKEDLIST_H_*/
    

    DoubleLinkedList.cpp

    #include "DoubleLinkedList.h"
    #include <cstring>
    
    DoubleLinkedList::DoubleLinkedList()
    {
    	init();
    }	
    /*
    DoubleLinkedList::DoubleLinkedList(const char* elements[] , unsigned int count)
    {
    	init();
    	addArray(elements,count,0);	
    }*/
    
    DoubleLinkedList::DoubleLinkedList(const char* elements[], unsigned int count, unsigned int offset=10)
    {
    	init();
    	addArray(elements,count,offset);
    }
    
    void DoubleLinkedList::init()
    {
    	//Liste auf Ursprung setzen
    	head = NULL; 
    	length = 0;
    }
    
    void DoubleLinkedList::push_back(const char* element)
    {
    	//Wenn Liste leer...
    	if(!head)
    	{
    		//...Element als head einfügen
    		head = new DoubleLinkedListElement(element,NULL,NULL);
    		head->prev = head;
    		head->next = head;
    	}
    	//Ansonsten...
    	else
    	{
    		//...ans Ende anhängen (vor dem head einfügen) 
    		DoubleLinkedListElement* dlle = new DoubleLinkedListElement(element,head->prev,head);
    		dlle->prev->next = dlle;
    		dlle->next->prev = dlle;	
    	}
    	//Zähler erhöhen
    	length++;
    }
    
    void DoubleLinkedList::push_front(const char* element)
    {
    	//Element am Ende anhängen (vor dem head)
    	this->push_back(element);
    	//head auf das bisher letzte Element der Liste setzen
    	head = head->prev;
    }
    
    const char* DoubleLinkedList::pop_back()
    {
    	//Liste ist leer
    	if(!head) return NULL;
    
    	//Zeiger auf das letzte Element der Liste
    	DoubleLinkedListElement* dlle = head->prev;
    
    	//Das einzige Element soll entfernt werden
    	if(dlle == head && dlle->next == head) head = NULL;
    
    	//Wert auslesen
    	const char* value = dlle->value;
    
    	//Zeiger der umgebenden Elemente ändern
    	dlle->prev->next = dlle->next;
    	dlle->next->prev = dlle->prev;
    
    	//Element löschen	 
    	delete dlle;
    	//Zähler dekrementieren
    	length--;
    	//Wert zurückgeben
    	return value;
    }
    
    const char* DoubleLinkedList::pop_front()
    {
    	if(!head) return NULL;
    
    	//head auf das 2. Element der Liste setzen
    	head = head->next;
    
    	//Letztes Element der Liste entfernen (vorheriger Head) und Wert zurückgeben
    	return pop_back();
    }
    
    void DoubleLinkedList::addArray(const char* elements[], unsigned int count, unsigned int offset=0)
    {	
    	//Elemente einfügen
    	for(unsigned int i = offset; i < count+offset; i++)
    	{
    		push_back(elements[i]);
    	}
    }
    
    void DoubleLinkedList::insert(unsigned int position,const char* element)
    {
    	//Wenn Liste leer oder Position zu groß...
    	if(!head || position > length)
    	{
    		//...am Ende anhängen
    		push_back(element);
    	}
    	//Ansonsten...
    	else
    	{
    		//...An entsprechender Position einsetzen
    		DoubleLinkedListElement* posElement = head;
    		for(unsigned int i = 0; i < position; i++) posElement = posElement->next;
    		DoubleLinkedListElement* dlle = new DoubleLinkedListElement(element,posElement->prev,posElement);
    		dlle->prev->next = dlle;
    		dlle->next->prev = dlle;
    		length++;
    	}
    }
    
    void DoubleLinkedList::remove(unsigned int position)
    {
    	//Liste ist leer, also nix tun
    	if(!head)return;
    	//Position == 0
    	else if(position == 0)
    	{
    		//...erstes Element wird entfernt
    		pop_front();
    	}
    	//Position ist zu groß...
    	else if(position > length-1)
    	{
    		//...letztes Element wird entfernt
    		pop_back();
    	}	
    	//Ansonsten
    	else
    	{
    		//...Position ermitteln
    		DoubleLinkedListElement* dlle = head;
    		for(unsigned int i = 0; i < position; i++)dlle=dlle->next;
    
    		//...Zeiger der Nachbarelemente tauschen
    		dlle->prev->next = dlle->next;
    		dlle->next->prev = dlle->prev;			
    		//Element löschen 
    		delete dlle;
    		//Zeiger dekrementieren
    		length--;
    	} 	
    }
    
    unsigned int DoubleLinkedList::getLength() const
    {
    	//Länge der Liste zurückgeben
    	return length;
    }
    
    bool DoubleLinkedList::operator==(const DoubleLinkedList& list) const
    {	
    	DoubleLinkedList self(*this);
    	unsigned int llength = list.getLength();
    
    	//Wenn die Länge ungleich ist, sind die Listen nicht gleich
    	if(length != llength) return false;
    
    	//Elementweises vergleichen
    	for(unsigned int i = 0; i < length; i++) 
    	{
    		//Wenn die Elemente nicht gleich sind, sind die Listen nicht gleich
    		if(strcmp(self[i],list[i]))return false; 
    	}
    
    	//Listen sind gleich
    	return true;
    }
    
    bool DoubleLinkedList::operator!=(const DoubleLinkedList& list) const
    {
    	return !operator ==(list);
    }
    
    DoubleLinkedList operator+(const DoubleLinkedList& list1 ,const DoubleLinkedList& list2)
    {
    	DoubleLinkedList tmp;	
    
    	//1. Liste zur tmp-liste hinzufügen
    	tmp+=list1;
    	//2. Liste zur tmp-liste hinzufügen
    	tmp+=list2;
    	//tmp-liste zurückgeben
    	return tmp;
    }
    
    DoubleLinkedList DoubleLinkedList::operator+=(const DoubleLinkedList& list)
    {
    	//Elementweises anhängen der Elemente
    	for(unsigned int i = 0; i < list.getLength(); i++)
    	{
    		this->push_back(list[i]);
    	}
    	//Gibt sich selbst zurück
    	return *this;
    }
    
    const char* DoubleLinkedList::operator[](unsigned int position) const
    {
    	//Liste leer oder Position zu groß
    	if(!head || position >= length)return NULL;
    
    	//Element an der gewünschten Position suchen
    	DoubleLinkedListElement* dlle = head;
    	for(unsigned int i = 0; i < position; i++) dlle=dlle->next;
    
    	//Wert des Elements zurückgeben
    	return dlle->value;
    }
    
    void DoubleLinkedList::print()
    {
    	//Alle elemente ausgeben 
    	DoubleLinkedList self = DoubleLinkedList(*this);
    	cout << "-----------------------\n"
    		 << "length: " << length << "\n"
    		 << "-----------------------" << endl;
    	for(unsigned int i = 0; i < length; i++)
    	{
    		cout << i << ": " << self[i] << endl;
    	}
    	cout << "-----------------------" << endl;
    }
    

    @Nexus: War wohl blind...Danke 😃



  • Du musst den Standardparameter im Header angeben. Wenn der Compiler die Test.cpp kompiliert, weiß er ja nicht, dass du in einer gänzlich anderen .cpp einen Standardwert für den letzten Parameter angegeben hast. Er sieht ja nur den Header, in dem 3 Parameter verlangt werden und es keinen Standardwert gibt, so dass man mit 2 Parametern aufrufen könnte.



  • Habs jetzt mal so geändert:

    #ifndef DOUBLELINKEDLIST_H_
    #define DOUBLELINKEDLIST_H_
    
    #include "DoubleLinkedListElement.h"
    #include <iostream>
    
    using namespace std;
    
    class DoubleLinkedList
    {	
    	friend class DoubleLinkedListElement;
    	friend DoubleLinkedList operator+(const DoubleLinkedList&, const DoubleLinkedList&);
    
    	private:
    		unsigned int length;
    		DoubleLinkedListElement* head;
    		void init();
    	public:	
    		DoubleLinkedList();
    		DoubleLinkedList(const char**, unsigned int ,unsigned int );
    		DoubleLinkedList(const char**, unsigned int);
    		void push_back(const char*);
    		void push_front(const char*);
    		const char* pop_back();
    		const char* pop_front();
    		void insert(unsigned int,const char*);
    		void addArray(const char*[] ,unsigned int ,unsigned int);
    		void remove(unsigned int);
    		unsigned int getLength() const;
    		bool operator==(const DoubleLinkedList&) const;
    		bool operator!=(const DoubleLinkedList&) const;
    		DoubleLinkedList operator+=(const DoubleLinkedList&);
    		const char* operator[](unsigned int) const;
    		void print();
    };
    
    //#include "DoubleLinkedList.cpp"
    
    #endif /*DOUBLELINKEDLIST_H_*/
    

    aber trotzdem:

    make -k all 
    /usr/bin/g++ -Wall -o DoubleLinkedList Test.o DoubleLinkedList.o DoubleLinkedListElement.o
    Test.o: In function `main':
    Test.cpp:(.text+0xc9): undefined reference to `DoubleLinkedList::DoubleLinkedList(char const**, unsigned int)'
    collect2: ld gab 1 als Ende-Status zurück
    make: *** [all] Fehler 1
    


  • Ja, jetzt hast du DoubleLinkedList::DoubleLinkedList(char const**, unsigned int) hinzugefügt, also hat der Compiler nix zu meckern wenn er test.cpp kompiliert, aber wo hast du denn DoubleLinkedList::DoubleLinkedList(char const**, unsigned int) implementiert? Daran knabbert der Linker jetzt.

    Das einzige was ich die ganze Zeit doch sagen möchte ist, du hättest im HEADER stehen haben müssen:

    DoubleLinkedList(const char**, unsigned int ,unsigned int someval = 5 );

    Dann hättest du auch keine zusätzliche Funktion deklarieren müssen, weil der Compiler dann, während er test.cpp kompiliert, weiß, dass der letzte Parameter einen default-Wert besitzt und man die Funktion somit auch mit 2 Parametern aufrufen kann.

    Und überhaupt, im Header solltest du allen Parametern einen Namen geben, das macht es demjenigen einfacher, die Bibliothek zu benutzen, wenn er sich nur den Header (= Die Schnittstelle zu den Objekten) anschaut.



  • Dann kann ich das also nicht weglassen?

    Ich dachte, dass das der Sinn der vordefinierten Parameter ist.

    Muss ich also den Konstruktor komplett anlegen, oder kann ich den Aufruf umleiten?



  • Siehe oben, hab das nachträglich nochmal etwas verdeutlicht.



  • Also was es mitzunehmen gilt: Der Compiler sieht immer nur eine einzige .cpp datei und alles was diese datei #include'd hat. Der hat keine Ahnung davon, dass eine andere .cpp datei irgendwelche Standardparameter definiert. Der Linker ist dann dafür zuständig die ganzen Symbole zwischen mehreren "Units" (=.o-Dateien) aufzulösen und den Implementierungen (= Dem Maschinencode) zuzuordnen. Mal etwas vereinfacht ausgedrückt. Und da Standardparameter etwas sind, das auf Compilerebene wirkt, ergibt sich dein beobachtetes Problem.



  • OK:
    1. Hab ich jetzt alle Parameter mit Namen angegeben.
    2. Der Konstruktor lautet jetzt so:

    DoubleLinkedList(const char* elements[], unsigned int count,unsigned int offest = 0);
    

    Das hatte ich auch schonmal, aber es klappt nicht:

    make -k all 
    /usr/bin/g++ -Wall -c DoubleLinkedList.cpp
    DoubleLinkedList.cpp:15: Fehler: Standardargument für Parameter 3 von »DoubleLinkedList::DoubleLinkedList(const char**, unsigned int, unsigned int)« angegeben
    DoubleLinkedList.h:20: Fehler: nach vorheriger Spezifikation in »DoubleLinkedList::DoubleLinkedList(const char**, unsigned int, unsigned int)«
    make: *** [DoubleLinkedList.o] Fehler 1
    make: Das Target »all« wurde wegen Fehlern nicht aktualisiert
    


  • Naja, jetzt kannst du ja (rein logisch gesehen) und musst (dem C++-Standard nach) die Spezifikation eines Standardwertes für den 3. Parameter in der Implementierung weglassen.



  • Ariol schrieb:

    make -k all 
    ...
    DoubleLinkedList.cpp:15: Fehler: Standardargument für Parameter 3 von »DoubleLinkedList::DoubleLinkedList(const char**, unsigned int, unsigned int)« angegeben
    ...
    

    Lies mal bitte alle Postings nochmal durch.

    Standardparameter müssen ausschließlich in der Deklaration angegeben werden. Der Compiler sagt aber das du in der "DoubleLinkedList.cpp" (und ich wette dort in der Definition) einen Standardwert angegeben hast.



  • Sorry, falsche Ausgabe gepostet:
    hier die richtige:

    make -k all 
    /usr/bin/g++ -Wall -o DoubleLinkedList Test.o DoubleLinkedList.o DoubleLinkedListElement.o
    Test.o: In function `main':
    Test.cpp:(.text+0xc9): undefined reference to `DoubleLinkedList::DoubleLinkedList(char const**, unsigned int)'
    collect2: ld gab 1 als Ende-Status zurück
    make: *** [all] Fehler 1
    

    .............

    Ok, der Fehler war ein fehlendes "make clean".

    Mann, das hätte ich schon vorgestern haben können.

    Vielen Dank für eure Hilfe. 😉 Jetzt klappt alles 👍


Anmelden zum Antworten