Pointer-To-Pointer



  • Hallo zusammen,

    warum lässt sich folgender Code nicht übersetzen ?????

    class A {};
    class B: public A {};

    int main(void)
    {
    B* pB = new B;
    A* pA = pB; // OK
    A** ppA = &pB; <- Hier ist das Problem ?
    }

    Ich bekomme folende Fehler:
    MSVC 8.0: error C2440: 'initializing' : cannot convert from 'B **__w64 ' to 'A **'
    gcc 3.3.3: cannot convert `B**' to `A**' in initialization

    Grüße,
    Philipp



  • Du kannst zwar einen "Zeiger auf abgeleitete Klasse" in einen "Zeiger auf Basisklasse" umwandeln, aber das geht nur in einer Ebene. Alles andere würde nur zu Problemen mit der Polymorphie, Objektgrößen etc führen.

    (btw, "A** ppA = &pA;" ist übrigens erlaubt, falls du es unbedingt brauchst)



  • ja das es nicht geht weiß ich, die Frage ist, WARUM genau.

    Weshalb spielt die Polymorphie eine Rolle ?

    und warum die Objektgröße ? Wie Du schreibst, in einer Ebene geht es (da ist die Objektgröße ja kein Problem) und dann kommt ja nur der Pointer hinzu und Pointer sind immer gleich groß, egal wo sie hinzeigen....

    Kannst Du das mal eventuell in einem konstruierten Beispiel zeigen, wo es Probelme mit der Polymorphie / Objekgröße geben würde (wenn der obige Code funktionieren würde) ? Danke !!

    Und was genau verbirgt sich hinter Deinem "etc" ?



  • darum:

    class base
    {
    public:
      // ...
    };
    
    class a : public base
    {
    public:
      // ...
    };
    
    class b : public base
    {
    public:
      // ...
    };
    
    // ...
    
    base* ptr = new a;
    b* ptr_2_b = ptr; // wäre falsch und ist daher nicht möglich
    

    ein zeiger auf eine basisklasse kann auch auf eine abgeleitete klasse zeigen. aber nicht umgekehrt. denn es gibt meistens mehrere abgeleitete klassen. woher soll der compiler dann wissen auf welchen typ der zeiger wirklich zeigt.

    schau dir mal dynamic_cast an 😉



  • dot schrieb:

    base* ptr = new a;
    b* ptr_2_b = ptr;
    

    was hat das mit der frage zu tun?



  • Hey dot,

    ok, alles klar.....wo ist der Zusammenhang mit dem oben genannten Problem ?? Ich habe nur Pointer vom Typ der Basisklasse...

    Die Frage war, WARUM geht es nicht. Das es nicht geht ist klar und wie ich es (für mein Problem hier) umgehen kann, auch.

    Aber mir ist nicht klar, warum der Compiler das nicht automatisch erkennt.
    Weil:

    B* pB = new B;
    A* pA = pB;

    A** ppA1 = &pA; <- geht, ist ein pointer auf einen A pointer, der auf ein B zeigt
    A** ppA2 = &pB; <- geht nicht, soll ein pointer sein, der auf einen B pointer zeigt, der auf ein B zeigt.

    Dabei kann aber ein b pointer auf ein B einem A pointer zugewiesen werden....



  • ich versteh nicht ganz wo das problem liegt. der compiler _könnte_ in diesem speziellen fall erkenne, dass es sich sicher um ein B handelt (und daher evtl. spezielle optimierungen machen, wie z.b. virtuelle funktionsaufrufe auflösen). das bedeutet aber nicht, dass er das immer kann. und warum sollte man ein konstrukt erlauben, nur weil es manchmal möglich ist, falls der compiler schlau genug ist. außerdem zwingt deinen compiler niemand dazu es nicht einfach zu erlauben, wäre dann halt kein standard c++ mehr.

    und das zweite (mit den doppelpointern) hat mit dem ersten doch nix zu tun!?



  • ich auch nicht......:-)

    Aber ich hoffe das CStoll seinen Beitrag etwas erläutert. Er scheint verstanden zu haben, um was es geht.



  • SRY: ich sollte mal etwas genauer lesen...

    also die frage war warum das nicht geht:

    B* b = new B;
    A** = &b;
    

    ganz einfach, weil das auch nicht geht:

    float* blub = new float;
    int** pi = &blub;
    

    wenn du einen pointer auf eine abgeleitete klasse in einen pointer auf eine basisklasse umcastest, kann es sein, dass der pointer dabei verändert werden muss (damit er auch wirklich auf das in der abgeleiteten klasse enthaltene objekt der basisklasse zeigt).

    B* b = new B;
    A** a = &b;
    

    wäre das möglich, würde in so einem fall *a auf das falsche objekt zeigen.



  • dot schrieb:

    SRY: ich sollte mal etwas genauer lesen...

    also die frage war warum das nicht geht:

    B* b = new B;
    A** = &b;
    

    ganz einfach, weil das auch nicht geht:

    float* blub = new float;
    int** pi = &blub;
    

    ...

    😮 😕
    Also Deine Erklärungsversuche in allen Ehren, aber die hilft hier nicht weiter.
    DAS

    float* blub = new float;
    int** pi = &blub;
    

    geht nicht, weil auch

    int* blub = new float;
    

    nicht geht => weil int und float keine polymorphen Typen sind.

    Gruß,

    Simon2.



  • jo, A* und B* sind auch keine polymorphen typen!?



  • ok, jetzt reden wir über dasselbe. Wie muss der pointer "verändert" werden, damit er auf das richtige Objekt zeigt ? (Pointer enthalten ja eigentlich nur eine Speicheradresse und für den Compiler haben sie noch einen "Typ").

    Wohlgemerkt, das B ja ein A ist !



  • jo, pointer enthalten eine speicheradresse. ein pointer auf ein B zeigt auf den anfang des B objektes. da B von A abgeleitet ist, enthält ein B objekt auch ein A (ein A objekt ist ja ein teil von einem B objekt). deshalb kann man einen zeiger auf ein B auch in einen zeiger auf ein A umwandeln. der zeiger wird einfach so verändert, dass er auf das A in dem B zeigt. üblicherweise steht das A einfach am anfang, dann wäre keine veränderung notwendig, aber das ist implementation defined (es ist also im standard nicht definiert, dass beim upcast der zeiger gleich bleibt). spätestens wenn mehrfachvererbung und virtuelle basisklassen im spiel sind führt kein weg drum herum.



  • Ok, vielleicht stelle ich mich mit dem Verstehen etwas blöde an, aber nochmal zurück zum Beispiel:

    class A {};
    class B: public A {};

    B* pB = new B;
    A* pA = new A;
    A* pAB = pB;

    Geht:
    A** ppA1 = &pA;
    logisch, da pA ein pointer vom Typ A* (auf ein A ist) und man mit einem A** darauf zeigen kann. Typprüfung nachvollziehbar

    A** ppA1 = &pAB;
    logisch, da pAB ein pointer vom Typ A* (auf ein B ist) und man mit einem A** darauf zeigen kann. Typprüfung nachvollziehbar.

    Geht nicht:
    A** ppA2 = &pB;
    weil pB ein pointer vom Typ B* (auf ein B ist) und man mit einem A** nicht darauf zeigen kann, weil:
    man mit einem pointer vom Typ B* nur auf ein B zeigen kann, nicht aber auf ein A (da das die Elternklasse ist). Man kann einem pointer vom Typ B* nicht einen pointer vom Typ A* zuweisen (außer mit dynamic_cast oder static_cast).

    Ist das so richtig verstanden ?



  • jo, ich denk schon



  • fast richtig verstanden wuerd ich sagen. Du kannst Zeiger nur auf den zugehoerigen Typen oder einen davon abgeleiteten Typen zeigen lassen. pA sollte also auf ein A oder einen von A abgeleiteten Typen zeigen (wie du richtig erkannt hast). ABER:
    ppA ist ein Zeiger auf A*. A* wiederum ist ein Zeiger und kein Nutzerdefinierter Typ, und daher kann man von A* auch nichts baleiten. ergo kann ppA einzig und allein auf A* Variablen zeigen. Die Typen A* und B* haben sogesehen rein garnichts miteinander gemeinsam.



  • Die Typen A* und B* haben sogesehen rein garnichts miteinander gemeinsam.

    jo, das war es auch, was mein beispiel mit dem int und dem float aussagen sollte. das beispiel war nur von mir leider "etwas ungut" (saublöd) gewählt^^ sry dafür



  • Hallo Jungs, speziell Pumuckl,

    Endlich, ok das ist eine wirklich gute und verständliche Begründung. Jetzt ist alles klar ! Danke !

    Das mit Objektgröße/Polymorphie hat mich so wie oben gepostet etwas irritiert.

    PS. bleibt immer noch die Frage, warum man C++ nicht so definiert hat, dass es aus dem Zusammenhang A<->B auch den Zusammenhang A*<->B* erkennen kann....aber das wäre wohl ein anderer Post



  • PhilippMaurer schrieb:

    Was für ein Schwachsinn !!! Natürlich kann ich einem pointer vom Typ B* einem pointer vom Typ A* zuweisen, mach ich oben ja sogar ! Immer noch ein Rätzel...

    meinst du einem pointer vom typ B* einen poiner vom typ A* zuweisen oder umgekehrt?
    falls ersteres:
    nein, nicht ohne cast

    PhilippMaurer schrieb:

    bleibt immer noch die Frage, warum man C++ nicht so definiert hat, dass es aus dem Zusammenhang A<->B auch den Zusammenhang A*<->B* erkennen kann....aber das wäre wohl ein anderer Post

    was sollte das:

    A** ppA = &pB;
    

    deiner meinung nach tun?



  • Einen Pointer auf einen B Pointer der auf ein B zeigt, aber vom Typ A**. Da A die Elternklasse von B ist, wären folgende "Zeigerketten" für mich logisch einwandfrei:

    A** -> A* -> A (geht in C++)
    A** -> A* -> B (geht in C++)

    A** -> B* -> B (geht nicht in C++)


Anmelden zum Antworten