c++, Verberung, warum krieg ich hier einen linker Fehler?



  • class CBaseClass
    {
    public :
    
       CBaseClass ()
       {
          GetStr ();
       }
    
       virtual CString GetStr ();
    };
    
    class CSubClass : public CBaseClass
    {
    public :
    
       CString GetStr ()
       {
          return "";
       }
    };
    

    SrvFileExport error LNK2019: Nicht aufgelöstes externes Symbol '"public: virtual class ATL::CStringT<char,class ATL::StrTraitATL<char,class ATL::ChTraitsCRT<char> > > __thiscall CBaseClass::GetStr(void)" (?GetStr@CBaseClass@@UAE?AV?CStringT@DV?CStringT@DV?StrTraitATL@DV?$ChTraitsCRT@D@ATL@@@ATL@@@A TL@@XZ)', verwiesen in Funktion '"public: __thiscall CBaseClass::CBaseClass(void)" (??0CBaseClass@@QAE@XZ)'



  • Weil zum Erstellen deiner vtable die Adresse der Funktion der jeweiligen Klasse bekannt sein muss, und die gibt es nur mit Implementierung. Das gilt auch für die Basisklasse!
    Also entweder CBaseClass::GetStr() definieren, oder CBaseClass::GetStr() pure virtuaL machen.



  • l'abra d'or schrieb:

    Weil zum Erstellen deiner vtable die Adresse der Funktion der jeweiligen Klasse bekannt sein muss, und die gibt es nur mit Implementierung. Das gilt auch für die Basisklasse!
    Also entweder CBaseClass::GetStr() definieren, oder CBaseClass::GetStr() pure virtuaL machen.

    Das heißt so??

    virtual CString GetStr () = 0;

    class CBaseClass
    {
    public :
    
       CBaseClass ()
       {
          GetStr ();
       }
    
       virtual CString GetStr () = 0;
    };
    
    class CSubClass : public CBaseClass
    {
    public :
    
       CString GetStr ()
       {
          return "";
       }
    };
    

    Da bekomme ich weiterhin einen linker Fehler 😞



  • Ich habe es gerade durch den GCC laufen lassen und der macht keinen Linkerfehler.



  • volkard schrieb:

    Ich habe es gerade durch den GCC laufen lassen und der macht keinen Linkerfehler.

    Hm... mist hab hier nen Visual Studio 2003 😞

    Muss ich da eine bestimmte Projekteinstellung haben?



  • Man ruft auch keine virtuellen Methoden im Konstruktor auf!!! Grundregel, absolut niemals nicht!!!
    Sry für die vielen Ausrufezeichen...
    Google sollte da ausreichend Erklärungen liefern.



  • Im Konstruktor von CBaseClass wird CBaseClass::GetStr() aufgerufen (nicht etwa, wie Du vielleicht annimmst, CSubClass::GetStr()), für die es keine Implementierung gibt. Grund ist, dass das CSubClass-Objekt zu dem Zeitpunkt noch nicht vollständig initialisiert ist.

    Methoden können übrigens rein virtuell sein und dennoch eine Implementierung besitzen.

    @volkard:
    Die Compiler verhalten sich unterschiedlich: Während MSVC nach der nicht vorhandenen Implementierung sucht, erstellt GCC eine Dummy-Implementierung, die das Programm mit der Meldung "pure virtual function called" abbricht.



  • gcc-4.4.3 (eigentlich ja ld aus binutils-2.20.1) bricht bei folgendem Programm

    struct Base {
        Base() {
            fun();
        }
    
        virtual void fun() =0;
    };
    
    struct Der : public Base {
        void fun() {
        }
    };
    
    int main() {
        Der d;
        Base *b = new Der;
    }
    

    mit diesem Fehler ab

    virt_fun_in_constr.cpp: In constructor »Base::Base()«:
    virt_fun_in_constr.cpp:4: Warnung: abstract virtual »virtual void Base::fun()« called from constructor
    /tmp/ccAz8GqY.o: In function `Base::Base()':
    virt_fun_in_constr.cpp:(.text._ZN4BaseC2Ev[Base::Base()]+0x1f): undefined reference to `Base::fun()'
    collect2: ld gab 1 als Ende-Status zurück
    


  • LordJaxom schrieb:

    @volkard:
    Die Compiler verhalten sich unterschiedlich: Während MSVC nach der nicht vorhandenen Implementierung sucht, erstellt GCC eine Dummy-Implementierung, die das Programm mit der Meldung "pure virtual function called" abbricht.

    Nichtmal das.
    Er sagt auch "undefined reference to CBaseClass::GetStr()". Ich habe vergessen, in der main so ein Objekt anzulegen.

    Aber mit meinen Debug-Einstellungen kommt was recht feines.

    D:\...\main.cpp||In constructor 'CBaseClass::CBaseClass()':|
    D:\...\main.cpp|11|warning: abstract virtual 'virtual std::string CBaseClass::GetStr()' called from constructor|
    


  • LordJaxom schrieb:

    Im Konstruktor von CBaseClass wird CBaseClass::GetStr() aufgerufen (nicht etwa, wie Du vielleicht annimmst, CSubClass::GetStr()), für die es keine Implementierung gibt. Grund ist, dass das CSubClass-Objekt zu dem Zeitpunkt noch nicht vollständig initialisiert ist.

    Methoden können übrigens rein virtuell sein und dennoch eine Implementierung besitzen.

    @volkard:
    Die Compiler verhalten sich unterschiedlich: Während MSVC nach der nicht vorhandenen Implementierung sucht, erstellt GCC eine Dummy-Implementierung, die das Programm mit der Meldung "pure virtual function called" abbricht.

    Ok danke für die Erklärungen. 🙂
    Ich find das n bissi doof, aber wenn das c++ so macht is es ok 🙂

    Ich komm von C# und da geht so etwas:

    public abstract class BaseClass
    {
       public BaseClass ()
       {
          GetStr ();
       }
    
       public abstract String GetStr ();
    }
    
    public class SubClass : BaseClass
    {
       public override String GetStr ()
       {
          return "";
       }
    }
    

    Der ruft dann tatsächlich die GetStr () von SubClass auf.

    Hatte Gedacht, da sich die Sprachen ähneln kann c++ so etwas auch.

    Der Grund weswegen ich frag, die Basisklasse hat Member für Parameter die jede Abgeleitete Klasse selber festlegt. Damit die nicht vergessen werden zu initialiseren dacht ich, ich ruf die Methode im Kontruktor auf.

    Ich müsste es also so machen??

    class CBaseClass
    {
    public :
    
       int m_Parameter1;
       int m_Parameter2;
       int m_Parameter3;
    
       CBaseClass ()
       {
          //Init (); // Geht nicht
       }
    
       virtual void Init () = 0;
    };
    
    class CSubClass : public CBaseClass
    {
    public :
    
       void Init ()
       {
          m_Parameter1 = 1;
          m_Parameter2 = 2;
          m_Parameter3 = 3;
       }
    };
    
    CSubClass SubClass;
    
    SubClass.Init ();
    

    Wenn jemand vergisst die Init aufzurfen dann klapperts 😞



  • In dem Fall macht man Konstruktor private und bietet eine statische factory-Methode an, die dann gefahrlos ein Objekt erstellen und danach die init()-Methode aufrufen kann.



  • Dafür sind doch Konstruktoren und Initialisierungslisten da...

    class base
    {
    protected:
       int m_Parameter1;
       int m_Parameter2;
       int m_Parameter3;
    
       explicit base(int p1=0, int p2=0, int p3=0)
       : m_Parameter1(p1)
       , m_Parameter2(p2)
       , m_Parameter3(p3) {}
    
    public :
       virtual std::string getstr() const = 0;
    };
    
    class derived : public base
    {
    public :
       derived() : base(1,2,3) {}
       std::string getstr() const; // muss noch definiert werden
    };
    
    :
    
    derived d;
    

    Achte auch darauf, dass die Definitionen von "Funktionen" innerhalb von Klassen inline impliziert.



  • Die Initialisierungslisten kenn ich schon, ist aber für meinen Fall nicht so gut.
    Eine Factory-Methode das klingt super 🙂

    Vielen Dank an alle die geholfen haben 🙂



  • Sieben schrieb:

    Die Initialisierungslisten kenn ich schon, ist aber für meinen Fall nicht so gut.

    Warum nicht? Du solltest sie Zuweisungen im Konstruktorrumpf oder gar in manuell aufzurufenden Init() -Funktionen vorziehen.


Anmelden zum Antworten