template class verlinken?



  • Hi Leute
    ich hab da folgendes Problem:
    ich hab mir eine template class geschrieben um das mal zu üben. Sie soll funktionieren wie ein Stack. Leider kriege ich das mit dem Linker nicht hin. Er erkennt die Methoden der Klasse nicht. Kann mir da einer hlefen?
    folgender Fehler:

    Workshop Klassen error LNK2001: Nichtaufgeloestes externes Symbol "public: __thiscall TStack<int>::~TStack<int>(void)" (??1?TStack@H@@TStack@H@@FQAE@XZ) Workshop Klassen error LNK2001: Nichtaufgeloestes externes Symbol "public: int __thiscall TStack::Pop(void)" (?Pop@?TStack@H@@$$FQAEHXZ)
    Workshop Klassen error LNK2001: Nichtaufgeloestes externes Symbol "public: bool __thiscall TStack<int>::isEmpty(void)" (?isEmpty@?TStack@H@@TStack@H@@FQAE_NXZ)WorkshopKlassenerrorLNK2001:NichtaufgeloestesexternesSymbol"public:bool__thiscallTStack<int>::Push(int)"(?Push@?FQAE\_NXZ) Workshop Klassen error LNK2001: Nichtaufgeloestes externes Symbol "public: bool \_\_thiscall TStack<int>::Push(int)" (?Push@?TStack@H@@$$FQAE_NH@Z)
    Workshop Klassen error LNK2001: Nichtaufgeloestes externes Symbol "public: __thiscall TStack<int>::TStack<int>(int)" (??0?TStack@H@@TStack@H@@FQAE@H@Z) Workshop Klassen error LNK2001: Nichtaufgeloestes externes Symbol "public: __thiscall TStack::~TStack(void)" (??1?TStack@H@@QAE@XZ)
    Workshop Klassen fatal error LNK1120: 6 unaufgeloeste externe Verweise

    der code dazu:

    /***********main**************/
    #include <iostream>
    #include "TStack.h"
    using namespace std;
    
    int main()
    {
    //TStack-test;
        TStack<int> stack;
        char eingabe = '0';
        int daten;
    
        do
        {
            cout << "Push <p>, Pop <r>, Ende <e>" << endl;
            cin >> eingabe;
    
            switch(eingabe)
            {
            case 'p':
                cout << "Daten: ";
                cin >> daten;
                if(stack.Push(daten)) 
                    ;
                else
                    cout << "Stack voll!" << endl;
                break;
    
            case 'r':
                if(stack.isEmpty())
                    cout << "Stack ist leer!" << endl;
                else
                    cout << "Daten: " << stack.Pop() << endl;
                break;
    
            case 'e':
            default:
                cout << "Ende" << endl;
            }
        }while(eingabe != 'e');
    }
    
    /*******TStack.h***************/
    
    #ifndef _TSTACK_H
    #define _TSTACK_H
    
    template <typename TDate> class TStack
    {
    private:
        TDate *daten;
        long anz;
        long maxanz;
    
    public:
        TStack(int size = 6);
        ~TStack();
    
        bool Push(TDate);
        TDate Pop(void);
        bool isEmpty(void);
    
    };
    
    #endif /* TStack.h */
    
    /*******TStack.ccp*********/
    
    #include "TStack.h"
    
    //Konstrucktor
    template<typename TDate> TStack<TDate>::TStack(int size)
    {
        daten = new TDate[size];
    
        if(daten)
        {
            anz = 0;
            maxanz = size;
        }
        else
        {
            anz = 0;
            maxanz = 0;
        }     
    }
    
    //Destrucktor
    template<typename TDate> TStack<TDate>::~TStack()
    {
        delete []daten;
    }
    
    //PUSH Packt Daten auf den Stack und prüft ob Platz noch da ist.
    template<typename TDate> bool TStack<TDate>::Push(TDate dat)
    {
        if(anz < maxanz)
        {
            daten[anz++] = dat;        
            return true;
        }
        else
            return false;
    }
    
    //Pop Entfernt Daten vom Stack
    template<typename TDate> TDate TStack<TDate>::Pop()
    {
    
        TDate dat = daten[--anz];
        return (dat);
    }
    
    //isEmpty prüft ob Stack leer ist
    template<typename TDate> bool TStack<TDate>::isEmpty()
    {
        return (anz == 0);
    }
    


  • Bei Templates funktioniert separate Übersetzung nicht. Implementier die Member im Header, oder binde die cpp-Datei im Header unten per #include ein.



  • template-Funktionen oder -Methoden musst du im Header implementieren.

    MfG MAV



  • da der Compiler den Code einer Template-Klasse zur Laufzeit generiert, muss der Compiler Zugriff auf die Implementierung haben zur Compilezeit.

    das bedeutet schlicht und einfach gesagt, dass du die Implementierung auch in den Header packen musst.

    (btw. das wär mal was für die FAQ, wenn das nicht eh schon da steht :))



  • Du musst die Methoden ebenfalls im Header definieren !!! Da man sie sonst nicht finden kann. Das Schlüsselwort Export soll da abhilfe schaffen, aber ich kenne keinen Compiler der das unterstützt.



  • danke das hat funktioniert. ich hab in 4 Büchern nachgelessen und ich bin da nicht draus schau geworden vorallem mit dem schlüsselwort export



  • Es gibt noch eine Lösung, die explizite Instantiirung einer Template-Klasse heisst.

    template class TStack<int>;
    

    Die Zeile kann man in die "TStack.cpp" Datei beifuegen.
    Na ja... Bei einigen Compilern funktioniert auch den Export von Template-Klasse.



  • desert pinguin schrieb:

    Es gibt noch eine Lösung, die explizite Instantiirung einer Template-Klasse heisst.

    template class TStack<int>;
    

    Die Zeile kann man in die "TStack.cpp" Datei beifuegen.
    Na ja... Bei einigen Compilern funktioniert auch den Export von Template-Klasse.

    gut das funktioniert auch wobei ich dann für jeden Datentyp jeweils solch eine Zeile einfügen muss:

    template class TStack<int>;
    template class TStack<double>;
    template class TStack<char>;
    ...
    

    da hab ich aber gleich ne Frage zu:
    Im meinem Buch steht folgendes:

    Die explizite Instanziierung ist eine Aufforderung an den Compiler, die gewünschte Spezialiesierung des Templates sofort zu erzeugen, auch wenn diese Spezialiesierung noch gar nicht benötigt wird.

    Heißt das jetzt mehr Arbeit für dem Compiler? Und wie wirkt sich das auf den fertigen Binärcode aus? (Wird die Datei größer?)



  • Ich würde sagen, dass nicht benutzte Teile nach wie vor rausgenommen werden - als wären es selbst geschriebene Klassen eben. Der große Nachteil ist, dass man einen TStack dann nur mit den Typen verwenden kann, die du in der .cpp auflistest. Das ist natürlich nicht gut für eine Containerklasse 😉
    Was die Compilerarbeit angeht, dauert das Kompilieren der .cpp schon seine Zeit. Dafür muss beim Benutzen des Stacks von anderswo her weniger Arbeit geleistet werden. Schätze ich mal...

    Ich weiß, danach wurde nicht gefragt, aber weil man das irgendwie so oft sieht: So wie die Klasse da steht, würde ich sie ziemlich buggy nennen, weil sie keinen Kopierkonstruktor und Zuweisungsoperator selbst definiert und die automatisch generierten unvermeidlich zum Absturz führen. Such mal nach der Regel der Großen Drei.



  • operator void schrieb:

    Ich weiß, danach wurde nicht gefragt, aber weil man das irgendwie so oft sieht: So wie die Klasse da steht, würde ich sie ziemlich buggy nennen, weil sie keinen Kopierkonstruktor und Zuweisungsoperator selbst definiert und die automatisch generierten unvermeidlich zum Absturz führen. Such mal nach der Regel der Großen Drei.

    Ich weis das das fehlt, ist ja nur ne Testklasse die mir den Umgang mit Templates verdeutlichen sollte. Der Rest war mit in diesem Code egal



  • Was die Groesse Binärcodes angeht, haengt das von dem konkreten Compiler ab.
    Ich habe ein kleines Beispiel mit gcc 3.2 compiliert. Beim Beifuegen von einiger expliziten Instantinierungen wurde die Datei groesser und beim Beifuegen von anderen blieb die Groesse dieselbe. Dieses Verhalten kann Ich nicht begreifen.


Anmelden zum Antworten