Casten von void-Pointer



  • Hi,

    ich weiß, das die Verwendung von void-Zeigern i. d. R. schlechter Stil ist.
    Es handelt sich auch eher um eine theoretische Frage als auf einen
    Anwendungsfall bezogen.

    #include <iostream>
    
    class A{
    public:
            int i;
            virtual ~A(){}
    };
    
    class B{
    public:
            int j;
            virtual ~B(){}
    };
    
    class C : public A, public B{
    public:
            int k;
            virtual ~C(){}
    };
    
    int main() {
            C o;
            o.i=1;
            o.j=2;
            o.k=3;
            void* vp = &o;
            A& a = *static_cast<A*>(vp);
            B& b = *static_cast<B*>(vp);
            C& c = *static_cast<C*>(vp);
            std::cout << a.i << std::endl;
            std::cout << b.j << std::endl;
            std::cout << c.k << std::endl;
            return 0;
    }
    

    Ausgabe:

    1
    1
    3
    

    Kann man bei einem void-Pointer, der auf irgendeine abgeleitete Klasse
    gezeigt hat, wieder zum Basis-Typ casten?

    Der richtige Cast bei einem void-Zeiger ist ja der static_cast.
    Ich finde es auch nicht verwunderlich, sondern absolut plausibel,
    dass es nicht geht.

    Deswegen nur die theoretische Frage:
    Ist es möglich?

    Gruß,
    Voidy



  • Wenn hinter deinem void-Zeiger ein Objekt vom Typ C steht castest du natürlich den void-Zeiger wieder zu einem Zeiger auf C, sonst hast du dir ja selbst ins Knie geschossen.

    A& a = *static_cast<C*>(vp);
    B& b = *static_cast<C*>(vp);
    C& c = *static_cast<C*>(vp);



  • Kurze Antwort: Nein.

    Lange Antwort: Die offensichtlichen Fälle, wo so etwas auseinanderfliegt, sind Mehrfachvererbung und virtuelle Vererbung. Generell erlaubt der Standard dem Compilerentwickler aber an dieser Stelle einen Freiraum, der, sobald Vererbung ins Spiel kommt, solche Schweinereien undefiniert lässt. Ich beziehe mich auf 4.10 (2) und (3):

    ISO/IEC 14482:2003 4.10 (2-3) schrieb:

    2 An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void.” The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject).

    3 An rvalue of type “pointer to cv D,” where D is a class type, can be converted to an rvalue of type “pointer to cv B,” where B is a base class (clause 10) of D. (...) The result of the conversion is a pointer to the base class sub-object of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

    sowie 10 (3) und (5):

    ISO/IEC 14882:2003 10 (3,5) schrieb:

    3 The order in which the base class subobjects are allocated in the most derived object (1.8) is unspecified. [Note: a derived class and its base class sub-objects can be represented by a directed acyclic graph (DAG) where an arrow means “directly derived from.” A DAG of sub-objects is often referred to as a “sub-object lattice.”

    Base
        ^
        |
     Derived
        ^
        |
     Derived2
    

    The arrows need not have a physical representation in memory. ]

    5 [Note: A base class subobject might have a layout (3.7) different from the layout of a most derived object of the same type. (...) ]

    Mit anderen Worten, wenn du

    struct B { };
    struct D : B { };
    
    // ...
    
    D d;
    B *pb = &d
    

    schreibst, ist nicht garantiert, dass pb == &d.



  • Danke für die Antworten!

    Der Cast nach C war mir klar, aber wenn ich die genaue Klasse nicht
    kennen würde, dann geht das ja nicht.

    Das Problem mit den unterschiedlichen Speicheradressen war mir ebenfalls bekannt.
    Ich dachte nur, dass evtl. über die vtable (, über die der Standard ja kein Wort verliert,)
    evtl. irgendwie eine Beziehung hergestellt werden kann.
    Meine initiale Idee war etwas wie:

    B& b = *static_cast<B*>(vp);
            b = dynamic_cast<B&>(b);
    

    wobei der erste Cast zum Finden der vtable gedacht war, die evtl.
    in allen Objekten an der gleichen Stelle ist.

    Naja...
    Ich bin allerdings gar nicht überrascht, dass es nicht geht.

    Ich betrachte die Frage damit als beantwortet.

    Danke euch beiden!

    Wahrscheinlich war die Frage eine bekloppte Idee 🙄


Anmelden zum Antworten