2 Stimmen

Abrufen eines Klassen-/Strukturobjekts über den Memberpointer

Ich habe eine C++ Struktur als

struct myStruct {
    int a;
    int b;
    int c;
}; 

myStruct b;
int *ptr = &b.c;

Wie kann ich myStruct Objekt zurück von ptr bekommen?

(Ich weiß, dass ich dies mit Zeigerarithmetik wie container_Of() in C tun kann. Im Grunde etwas wie

reinterpret_cast<myStruct*>(reinterpret_cast<char *>(ptr) - offsetof(myStruct, c));

Ich frage, ob es einen empfohlenen/eleganten Weg gibt?)

5voto

Daniel Earwicker Punkte 111630

Es gibt sicherlich keine empfohlene Methode, da dies in C++ überhaupt nicht empfohlen wird. Dies ist eine der Fragen, bei denen die korrekte Antwort lauten muss: "Tun Sie das nicht!"

Der Grund für die Verwendung von C++ anstelle von C ist, dass Sie die Struktur der Daten in Klassen kapseln wollen, für die sinnvolle Operationen definiert sind, anstatt dem gesamten Programm Kenntnisse über das interne Layout der Datenstrukturen zu geben.

Das heißt, die offsetof Technik, die Sie beschreiben, wird auf einfache alte Datenobjekte funktionieren, weil sie nicht anders als C structs sind.

3voto

nulldevice Punkte 434

Denn ptr keine Kenntnis von seiner überlagernden Struktur hat, gibt es meines Erachtens keinen eleganten Weg, um zurück zu myStruct . Ich kann nur empfehlen, dies nicht zu tun!

1voto

CB Bailey Punkte 693084

Ihr reinterpret_cast Lösung ist der Standardweg, um das zu erreichen, was Sie wollen. Der Grund dafür, dass mindestens ein Casting erforderlich ist, liegt darin, dass die Operation, die Sie durchführen möchten, von Natur aus unsicher ist. Offensichtlich ist nicht jeder Zeiger auf eine int ist ein Zeiger auf das dritte Glied einer myStruct daher kann es keine einfache typsichere Methode zur Durchführung der Konvertierung geben.

1voto

Dan Punkte 10034

Ihr Text und Ihr Beispiel verwenden eine POD-Struktur; Ihr Titel spricht jedoch auch von Klassen. Die folgende Antwort funktioniert nicht für Nicht-POD-Typen.

Für POD-Typen, siehe die "Makro "offsetof ....

Finden Sie den Offset, verwenden Sie, dass & Zeiger Arithmetik zu sagen, ein Zeichen * die Basis zurück.

0voto

Sie können vielleicht die Tatsache ausnutzen, dass der Compiler bei einem Cast von einem Zeiger auf eine Basisklasse, die nicht die einzige Basisklasse ist, den Zeiger so anpasst, dass er auf den Anfang der Superklasse zeigt. Wenn Ihr "c"-Mitglied also am Anfang einer Basisklasse existiert, könnten Sie einen Trick anwenden wie:

struct myStructBase1 { int a; };
struct myStructBase2 { int b; };
struct myStructBase3 { int c; };

struct myStruct : public myStructBase1,myStructBase2,myStructBase3 {
    int d;
};

int main() {
    myStruct my;

    int * bptr = &my.b;
    int * cptr = &my.c;
    // The only dirty part in the code...
    myStructBase2 * base2ptr = reinterpret_cast<myStructBase2*> (bptr);
    myStructBase3 * base3ptr = reinterpret_cast<myStructBase3*> (cptr);

    // In each (myStruct*) cast the pointers are adjusted to point to the super class.
    cout << &my << endl <<
            (myStruct*) base2ptr << endl <<
            (myStruct*) base3ptr << endl;
    return 0;
}
// Output:
// 0xbfbc779c
// 0xbfbc779c
// 0xbfbc779c

Die Voraussetzung dafür ist, dass dies funktioniert: Wenn ein Mitglied das erste Mitglied in seiner Klasse ist, dann ist seine Adresse in einem Objekt gleich der Adresse des Objekts (dieser Klasse). Ich bin mir nicht sicher, ob das wahr ist.

EDIT: Es sollte gelten, wenn die Wrapper-Basisklassen PODs sind. Nach folgenden Änderungen:

struct myStructBase1 { int a; virtual void g() {} };
struct myStructBase2 { int b; virtual void f() {} };
struct myStructBase3 { int c;  };
struct myStruct : public myStructBase1,myStructBase2,myStructBase3 {
    int d;
    virtual void h() {}
};

Die Ausgabe ist:

0xbfa305f4
0xbfa305f8
0xbfa305f4

Für das c-Mitglied gilt die Einschränkung weiterhin. Im Allgemeinen lautet die Antwort also: Ja, es gibt eine alternative Möglichkeit. Allerdings ist die Definition einer Basisklasse für jedes "reversible"-Mitglied wahrscheinlich nicht der richtige Weg.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X