undefined reference to "constructor"



  • guten abend,

    seit einiger zeit bin ich daran, mir einerseits zu übungszwecken, andereseits auch aus praktischen gründen, eine klasse zu schreiben, die mir den umgang mit matrizen erleichtern soll.

    dazu habe ich folgende quellcodes geschrieben:

    matrix.h

    #ifndef MATRIX_H
    #define MATRIX_H
    
    template <typename T>
    class matrix{
    	private:
    		int n;
    		int m;
    		T **mat;
    	public:
    		matrix();
    		matrix(int,int);
    		matrix(int,int,T);
    		~matrix();
    		T& operator()(int,int);
    };
    
    #endif
    

    matrix.cpp

    #include "matrix.h"
    #include <iostream>
    
    template <typename T>
    matrix<T>::matrix(){
    	n=0;
    	m=0;
    	mat=0;
    }
    
    template <typename T>
    matrix<T>::matrix (int nn, int mm){
    	n=nn;
    	m=mm;
    	mat = new T* [n];
    
    	for (int i=0; i<n; i++){
    		mat[i]=new T [m];
    	}
    }
    
    template <typename T>
    matrix<T>::matrix (int nn, int mm, T val){
    	matrix(nn,mm);
    
    	for (int i=0; i<nn; i++)
    		for (int j=0; i<mm; j++)
    			mat[i][j]=val;
    }
    
    template <typename T>
    matrix<T>::~matrix(){
    	for (int i=0; i<n; i++){
    		delete [] mat[i];
    	}
    	delete [] mat;
    }
    
    template <typename T>
    T& matrix<T>::operator()(int nn, int mm){
    	if (nn>=n or mm>=m)
    		std::cerr << "Indices out of range!" << std::endl; 
    	return mat[nn][mm];
    }
    
    #include <iostream>
    #include "matrix.h"
    #include <complex>
    using namespace std;
    
    int main (){
    	matrix< double > mat;
    	return NULL;
    }
    

    makefile

    FILES=main.cpp matrix.cpp
    OBJ=main.o matrix.o
    TOBJ=maint.o matrixt.o
    CXX=c++
    
    .cpp.o :
    	$(CXX) -O2 -c $*.cpp
    
    all: $(OBJ)
    	$(CXX) -o main.x $(OBJ)
    test: $(TOBJ)
    	$(CXX) -o maint.x $(TOBJ)
    clean:
    	rm -f *.o *.x
    

    Leider bekomm ich beim compilieren die fehlermeldung:

    c++ -O2 -c main.cpp
    c++ -O2 -c matrix.cpp
    c++ -o main.x main.o matrix.o
    main.o: In function `main':
    main.cpp:(.text+0x39): undefined reference to `matrix<double>::matrix()'
    main.cpp:(.text+0x41): undefined reference to `matrix<double>::~matrix()'
    collect2: ld gab 1 als Ende-Status zurück
    make: *** [all] Fehler 1
    

    es wird sich mit sicherheit nur um einen kleinen fehler handeln, aber ich komm nicht darauf.
    es wär schön, wenn jemand eine kurze und elegante lösung hat. 😉

    vielen dank schonmal



  • Template-Definitionen darfst du nicht in eine eigene Übersetzungseinheit auslagern - die muß der Compiler in Reinform sehen, wenn das Template insanziiert wird in der main.cpp).

    PS: Dieses gegenseitige Aufrufen der Konstruktoren untereinander funktioniert in C++ nicht, da solltest du das Anlegen des Arrays in eine Hilfsmethode auslagern.

    PPS: Und denk an die Regel der "Großen Drei", wenn du wirklich selber mit dem Speicher hantieren willst.



  • CStoll schrieb:

    Template-Definitionen darfst du nicht in eine eigene Übersetzungseinheit auslagern - die muß der Compiler in Reinform sehen, wenn das Template insanziiert wird in der main.cpp).

    d.h. ich muss die funktionen direkt in der klassendefinition inplementieren?
    ich probiers gleich mal aus.



  • Entweder du schreibst das ganze direkt in die Header-Datei, oder du inkludierst die Implementationsdatei in die Header-Datei. Mache ich meist so, zwecks Überblick.

    Es gibt noch eine Möglichkeit, diese unterstüzt aber kaum ein Compiler.

    Lg freeG



  • beatbasser schrieb:

    d.h. ich muss die funktionen direkt in der klassendefinition inplementieren?
    ich probiers gleich mal aus.

    Nein, das heißt es nicht. Es heißt, dass Du die Definitionen nur mit in den Header packen musst -- nicht unbedingt innerhalb der Klassendefinition. Definierst Du Funktionen innerhalb einer Klassendefinition sind die automatisch inline.

    Der Ansatz, eine Resource wie Speicher in einem Objekt zu kapseln, ist löblich. Allerdings kannst Du es Dir auch noch viel einfacher machen, indem Du statt einem Zeiger, dessen Kopiersemantik dir ja offensichtlich nicht gefällt, da Du Kopierkonstruktor, Destruktor & co überschreiben tust/müsstest, einfach einen vector<T> als Datenmitglied benutzt. Ui! Langer Satz. Ich hoffe, Du verstehst, was ich meine.



  • vielen dank erstmal,

    ihr habt mir hier wirklich geholfen.

    ich geb zu, dass die aufgabe im zusammenhang mit der stl nicht wirklich die sinnvollste ist, sollte sie aber auch nicht sein, sondern mir eher eine übung. deswegen auch der umgang mit den zeigern. sicherlich könnte ich auch auch in der klasse direkt ein array deklarieren und mit dem dann arbeiten, dann hätte ich den ganzen ärger nicht, aber auch nicht den spaß 😉

    ich werde den punkt mit den "großen drei" noch berücksichtigen. das war mir so nicht bewusst, wie wichtig der copy-contructor ist.

    für alle, die ein ähnliches problem haben und später über diesen threat stolpern:

    ich habe, ausgehend von den codes, lediglich ein #include "matrix.cpp" in der header-datei unter der klassendefinition hinzugefügt, #include "matrix.h" aus der matrix.cpp entfernt und diese datei auch nicht separat kompiliert.



  • beatbasser schrieb:

    ich habe, ausgehend von den codes, lediglich ein #include "matrix.cpp" in der header-datei unter der klassendefinition hinzugefügt, #include "matrix.h" aus der matrix.cpp entfernt und diese datei auch nicht separat kompiliert.

    Das macht man eigentlich nicht. Praxis ist die, Definitionen von Funktionstemplates entweder im Header zu lassen, oder per #include im Header eine Datei mit einer anderen Endung als cpp einzubinden, weil diese Datei keine eigene Übersetzungseinheit sein muss/sollte.



  • ich finde es auch komisch, dass ich auf diese art und weise kein eigene objekt-datei für die member-funktionen erzeuge, aber im moment ist es die einzige lösung, die ich habe.

    wie wäre denn die wirklich saubere variante zu realisieren?



  • beatbasser schrieb:

    wie wäre denn die wirklich saubere variante zu realisieren?

    Wurde das hier nicht schon mehrfach gesagt (inklusive meines letzten Beitrags)?!



  • ich versteh dass jetzt so, dass die elegante variante ist, die template-defintionen direkt in den header zu schreiben.

    der übersichtlichkeit halber hätte ichs gern in einer seperaten datei. sollte ja nicht das problem sein, die dann einfach per #include einzubinden. ist ja quasi das gleiche.
    kurz, ob ichs direkt in header schreibe, oder so inkludiere, sollte das gleiche sein. berichtigt mich, wenns nicht stimmt.

    meine letzte frage, die sich also noch stellt. kann ich eine seperate objekt-datei erzeugen, die ich dann mit dem hauptprogramm linke? und wenn ja, wie?
    ich stelle die frage, weil es ja auch möglich sein sollte, ein .o datei schon fertig zu haben, die ich dann nicht jedes mal neu kompilieren muss.



  • beatbasser schrieb:

    ich versteh dass jetzt so, dass die elegante variante ist, die template-defintionen direkt in den header zu schreiben.

    der übersichtlichkeit halber hätte ichs gern in einer seperaten datei.

    Ja, das ist elegant. Machs mit einer .inl-Datei, die du am Schluss der .hpp-Datei inkludierst. Nicht dass dies krümelkacker nicht schon mehrmals gesagt hätte...

    beatbasser schrieb:

    meine letzte frage, die sich also noch stellt. kann ich eine seperate objekt-datei erzeugen, die ich dann mit dem hauptprogramm linke?

    Nein, das geht nicht, da der Compiler die Template-Parameter erst zur Instanziierungszeit einsetzen kann. Du kannst höchstens einzelne Instanziierungen separat kompilieren.



  • beatbasser schrieb:

    der übersichtlichkeit halber hätte ichs gern in einer seperaten datei. sollte ja nicht das problem sein, die dann einfach per #include einzubinden. ist ja quasi das gleiche.
    kurz, ob ichs direkt in header schreibe, oder so inkludiere, sollte das gleiche sein. berichtigt mich, wenns nicht stimmt.

    Klar kannst du das machen, nur solltest du die Datei nicht CPP nennen 😉

    meine letzte frage, die sich also noch stellt. kann ich eine seperate objekt-datei erzeugen, die ich dann mit dem hauptprogramm linke? und wenn ja, wie?
    ich stelle die frage, weil es ja auch möglich sein sollte, ein .o datei schon fertig zu haben, die ich dann nicht jedes mal neu kompilieren muss.

    Nein, das geht nicht, außer du weißt schon vorher alle Datentypen, mit denen das Termplate jemals instantiiert werden soll.
    Das Template ist nicht alleinstehend verwendbar (im Gegensatz zu z.B. Generics aus C#), sondern erst wenn der Compiler weiß, was er für die Template-Parameter einsetzen soll. Darum benötigt er im Hauptprogramm den Quelltext des Templates.

    (theoretisch ist im Standard noch export vorgesehen, aber das hat afair noch kaum ein Compiler umgesetzt)



  • CStoll schrieb:

    (theoretisch ist im Standard noch export vorgesehen, aber das hat afair noch kaum ein Compiler umgesetzt)

    Und in c++-11 wird es auch wieder aus dem Standard entfernt.



  • CStoll schrieb:

    (theoretisch ist im Standard noch export vorgesehen, aber das hat afair noch kaum ein Compiler umgesetzt)

    Im alten Standard ists noch vorgesehen. Umgesetzt hats genau ein einziger Compiler (Comeau), und die haben da 2 Mannjahre an Entwicklung reingesteckt, nur um das export-Feature zu implementieren und zu zeigen, dass es keine nennenswerte Verbesserung bringt.
    Wie von manni66 beschrieben wirds aus dem C++0x-Standard komplett rausgenommen, mit dem Einverständnis der Comeau-Entwickler, die ja dadurch dann die 2 Jahre endgültig in den Sand gesetzt haben.

    SIehe dazu auch hier:
    http://herbsutter.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/



  • man könnte behaupten, dass manche hier ihren namen nicht umsonst haben 😉
    ich bin mal ganz naiv davon ausgegangen, dass es doch egal ist, wie ich das kind nenne, solang ich weiß wie es heißt.

    trotzdem, danke an die vielen hilfreichen tipps. ich bin mit der klasse schon fast soweit, wie ich sie (im moment) haben will.

    ich denke, alles was jetzt hier noch kommt, wird mir zu hoch.
    hauptsache, es funktioniert 😉



  • pumuckl schrieb:

    Im alten Standard ists noch vorgesehen. Umgesetzt hats genau ein einziger Compiler (Comeau), und die haben da 2 Mannjahre an Entwicklung reingesteckt, nur um das export-Feature zu implementieren und zu zeigen, dass es keine nennenswerte Verbesserung bringt.

    Umgesetzt hat es EDG, nicht Comeau. Comeau verwenden EDGs Frontend, und bieten das Feature in ihrem Compiler an, mehr nicht.

    Und gedauert hat es 1.5 Jahre für Design/Planung + 3 Mannjahre Entwicklungszeit.

    siehe: http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2003/n1426.pdf - "2.2 (Prohibitively) Expensive to implement"


Log in to reply