Zugriff auf Methode der abgeleiteten Klasse!?



  • Hallo ihr Profis,

    Habe hier folgenden Bsp. Code:

    CElement *pElem;     //CElement ist die Basisklasse
    
    	pElem = new CNode;   //CNode wurde von CElement abgeleitet
    	pElem = new CNode;
    	pElem = new CNode;
    
    	pElem[2].setActive();   //setActive = Methode der Basisklasse
    
    	if(pElem[2].isActive()) cout << "true";  //isActive = Methode der Basisklasse
    	else cout << "false";
    
    	pElem[0].test();   //test = Methode der abgeleiteten Klasse CNode, hier motzt der Compiler!
    

    Habe oben im Code kurz kommentiert wo das Problem liegt, hier ist noch der Error vom Compiler (VC++):

    error C2039: 'test' : is not a member of 'CElement'

    Kann mir jemand sagen wieso er die Methode nicht kennt, wenn das "pElem[x]" vom Typ CNode ist? Was müsste ich ändern damit es geht?

    Wichtig ist eben für mich dass ich die Elemente zur Laufzeit erstelle, dh ich weiss nie wann welches Element kommt (hier nur CNode, aber es gibt noch 6 andere, welche alle von CElement abgeleitet sind).

    Danke



  • bsash schrieb:

    Hallo ihr Profis,

    Habe hier folgenden Bsp. Code:

    CElement *pElem;     //CElement ist die Basisklasse
    /*
    	pElem = new CNode;   //CNode wurde von CElement abgeleitet
    	pElem = new CNode;
    	pElem = new CNode;*/
    pElem = new CNode[3];
    
    	pElem[2].setActive();   //setActive = Methode der Basisklasse
    	
    	if(pElem[2].isActive()) cout << "true";  //isActive = Methode der Basisklasse
    	else cout << "false";
    
    	pElem[0].test();   //test = Methode der abgeleiteten Klasse CNode, hier motzt der Compiler!
    

    Du kannst nicht von einer Basisklasse eine Funktionaufrufen,
    die nicht in dieser Deklariert ist. Die Funktion test kennt
    CElement nicht...

    Devil



  • Hmm, ich versteh dich nicht oder du mich nicht! :p

    Ich versuche es nun etwas zu verdeutlichen:

    CElement *pElem;     //CElement ist die Basisklasse 
    
    pElem = new CNodeA; 
    pElem = new CNodeB; 
    pElem = new CNodeC; //Alle drei Nodes wurden von CElement abgeleitet
    
    //Ich greife nun über den Index auf die jeweilige Instanz zu
    pElem[2].setActive();   //setActive = Methode der Basisklasse 
    
    if(pElem[2].isActive()) cout << "true";  //isActive = Methode der Basisklasse 
    
    pElem[0].test();   //test = Methode der abgeleiteten Klasse CNodeA, hier motzt der Compiler!
    

    Mein Problem:
    In pElem[0] ist die Instanz von CNodeA enthalten. Ich kann aber keine Methoden von CNodeA aufrufen! Das sollte doch möglich sein? Schnalle nicht was ich falsch mache... 😞



  • Also zuerst mal:

    bsash schrieb:

    CElement *pElem;     //CElement ist die Basisklasse 
    
    pElem = new CNodeA; 
    pElem = new CNodeB; 
    pElem = new CNodeC; //Alle drei Nodes wurden von CElement abgeleitet
     
    //Ich greife nun über den Index auf die jeweilige Instanz zu
    pElem[2].setActive();   //setActive = Methode der Basisklasse
    

    Das ist Unsinn. pElem[2] gibt es nicht, du hast kein Array alloziert. Du hast nur dreimal verschiedene Sachen alloziert und jedesmal dem Zeiger pElem zugewiesen, dass bedeutet erstens, dass pElem auf ein CNodeC zeigt, und zweitens, dass die CNodeA und CNodeB Objekte verloren sind und nicht mehr deletet werden können.

    In pElem[0] ist die Instanz von CNodeA enthalten. Ich kann aber keine Methoden von CNodeA aufrufen! Das sollte doch möglich sein? Schnalle nicht was ich falsch mache... 😞

    Der statische Typ von pElem[0] ist CElement. Der Compiler versucht jetzt einen Member namens test in CElement zu finden, was natürlich nicht klappt. Das statische Typsystem von C++ ist etwas, was man einfach akzeptieren muss ... Man kann die Typen natürlich umgehen, indem man castet:

    static_cast<CNodeA*>(pElem)[0].test();
    

    Das ist aber mit Vorsicht zu genießen. Wenn pElem nicht den tatsächlichen (oder dynamischen) Typ CNodeA hat, hat diese Konstruktion undefiniertes Verhalten.
    Ansonsten denke ich, dass dein Design an dieser Stelle eine Schwäche hat. Warum musst du die Funktion an der Stelle aufrufen? Steht CNodeA wirklich in einer IS-A Beziehung zu CElement? Was passiert, wenn der dynamische Typ eine andere Ableitung von CElement ist?



  • Das ist Unsinn. pElem[2] gibt es nicht, du hast kein Array alloziert. Du hast nur dreimal verschiedene Sachen alloziert und jedesmal dem Zeiger pElem zugewiesen, dass bedeutet erstens, dass pElem auf ein CNodeC zeigt, und zweitens, dass die CNodeA und CNodeB Objekte verloren sind und nicht mehr deletet werden können.

    Das denke ich nicht:

    CElement *pElem;
    
    	pElem = new CNode;
    	pElem = new CNode;
    	pElem = new CNode;
    	pElem = new CNode;
    
    	pElem[1].setActive();
    	pElem[2].setActive();
    
    	if(pElem[0].isActive()) cout << "true";
    	else cout << "false";
    
    	if(pElem[1].isActive()) cout << "true";
    	else cout << "false";
    
    	if(pElem[2].isActive()) cout << "true";
    	else cout << "false";
    
    	if(pElem[2].isActive()) cout << "true";
    	else cout << "false";
    

    Die Ausgabe hier ist false true true false, ich denke nicht dass da etwas überschrieben wird!

    Das mit dem Cast würde zwar gehen, aber ich weiss nicht vorher welches Objekt ich habe.
    Was ich hier versuche ist nichts anderes als dyn. Binden. Ich kenns von Java, da siehts so aus:

    CElement elem;
    
    elem = new CNodeA; 
    
    elem.test(); //test ist hierbei die Methode von CNodeA
    


  • Wir denken das aber schon. Du hast einen Zeiger pElem und dem weist Du immer wieder Speicheradressen zu. Es gibt in diesem Code kein Array!

    Zum nächsten Problem: Du hast richig erkannt, daß der cast problematisch ist, weil Du ja nicht weißt welches Objekt es ist. Große Preisfrage: Wenn Du's nicht weißt, woher soll es denn dann der Compiler wissen???

    Virtuelle Methoden gehen in C++ so:

    class Base
    {
    public:
      virtual void f() {cout << "Basis"<<endl;}
      virtual ~Base();
    }
    
    class Derived1 : public Base
    {
      void f() { cout << "Derived1" << endl;}
    }
    
    class Derived2 : public Base
    {
      void f() { cout << "Derived2" << endl;}
    }
    
    int main()
    {
      Base * pb[3];
      pb[0]=new Base;
      pb[1]=new Derived1;
      pb[2]=new Derived2;
    
      for(int i=0; i<3; ++i)
        pb[i]->f();
    
      for(int i=0; i<3; ++i)
        delete pb[i];
    }
    

    MfG Jester



  • bsash schrieb:

    Das denke ich nicht:

    Na wenn das so ist. Tut mir leid, dass ich dir deine Zeit gestohlen habe.



  • Danke Jester, so klappts! 🙂

    D.h. ich muss wohl alle Methoden die ich in den abgeleiteten Klassen verwende, in der Basisklasse als virtuell definieren. Es gibt doch da noch abstrakte Methoden. Nämlich dann wenn ich die Methode in der Basisklasse NIE brauche, also müsste das so aussehen:

    virtual void f() = 0;
    

    Richtig? Somit wird meine CElement Klasse auch automatisch abstrakt? Mach aber insofern nichts, da ich keien Instanzen davon benötige.

    Wieso muss ich hier jetzt eigentlich mit "->" auf die Methoden zugreifen?



  • Den -> brauchst Du, weil Du über einen Pointer zugreifst. Das ist sozusagen ne kurzschreibweise für

    (*p[i]).f();

    Nämlich erst derefernzieren und dann mit . auf die Methoden zugreifen.

    MfG Jester


Anmelden zum Antworten