2012-06-15 12 views
7

Ich weiß, dass das Speicherlayout der Mehrfachvererbung nicht definiert ist, also sollte ich mich nicht darauf verlassen. Kann ich mich jedoch in einem speziellen Fall darauf verlassen? Das heißt, eine Klasse hat nur eine "echte" Superklasse. Alle anderen sind "leere Klassen", d. H. Klassen, die weder Felder noch virtuelle Methoden haben (d. H. Sie haben nur nicht-virtuelle Methoden). In diesem Fall sollten diese zusätzlichen Klassen nichts zum Speicherlayout der Klasse hinzufügen. (Konkreter, in der C++ 11 Formulierung, hat die Klasse Standard-Layout)C++ Mehrfachvererbung Speicherlayout mit "Leere Klassen"

Kann ich daraus schließen, dass alle Superklassen keinen Offset haben? Hier z.B .:

#include <iostream> 

class X{ 

    int a; 
    int b; 
}; 

class I{}; 

class J{}; 

class Y : public I, public X, public J{}; 

int main(){ 

    Y* y = new Y(); 
    X* x = y; 
    I* i = y; 
    J* j = y; 

    std::cout << sizeof(Y) << std::endl 
        << y << std::endl 
        << x << std::endl 
        << i << std::endl 
        << j << std::endl; 
} 

, Y ist die Klasse mit X die einzige wirkliche Basisklasse zu sein. Die Ausgabe des Programms (wenn auf Linux mit g ++ 4.6 kompiliert) ist wie folgt:

0x233f010

0x233f010

0x233f010

0x233f010

Wie ich festgestellt habe, gibt es keine poi nter Anpassung. Aber ist diese Implementierung spezifisch oder kann ich mich darauf verlassen? Wenn ich ein Objekt vom Typ I (und ich weiß, dass nur diese Klassen existieren) bekomme, kann ich dann reinterpret_cast verwenden, um es in X zu konvertieren?

Meine Hoffnungen sind, dass ich mich darauf verlassen konnte, weil die Spezifikation besagt, dass die Größe eines Objekts mindestens ein Byte sein muss. Daher kann der Compiler kein anderes Layout auswählen. Wenn es I und J hinter den Mitgliedern von X layout, dann wäre ihre Größe Null (weil sie keine Mitglieder haben). Daher ist die einzige vernünftige Wahl, alle Superklassen ohne Offset auszurichten.

Bin ich richtig oder spiele ich mit dem Feuer, wenn ich reinterpret_cast von I bis X hier benutze?

+3

Speicherlayout der Vererbung, einzelne oder mehrere, leere Basen oder nicht, ist nicht definiert. –

+1

Ich würde sagen, du spielst mit Feuer, weil du versuchst, ein sehr seltsames Konstrukt zu machen, das sicherlich jedem, der es in Zukunft verstehen muss, Schmerz und Elend bringt, auch wenn es beim Upgrade nicht kaputt geht Dein Übersetzer! – Rook

+0

Zum Glück sind Ihre Aussagen in C++ 11 nicht mehr korrekt, siehe die Antwort :) – gexicide

Antwort

8

In C++ 11 muss der Compiler die Empty Base-Class Optimization für Standardlayout Typen verwenden. siehe https://stackoverflow.com/a/10789707/981959

Für Ihr spezielles Beispiel alle Typen sind Standardklassen Layout und keine gemeinsamen Basisklassen oder Mitglieder (siehe unten), so können Sie verlassen sich auf dieses Verhalten in C++ 11 (und in der Praxis Ich denke, dass viele Compiler diese Regel bereits befolgt haben, sicherlich G ++, und andere nach der Itanium C++ ABI.)

Ein Vorbehalt: stellen Sie sicher, dass Sie keine Basisklassen des gleichen Typs haben, weil sie an verschiedenen Adressen sein müssen, z.B

struct I {}; 

struct J : I {}; 
struct K : I { }; 

struct X { int i; }; 

struct Y : J, K, X { }; 

#include <iostream> 

Y y; 

int main() 
{ 
    std::cout << &y << ' ' << &y.i << ' ' << (X*)&y << ' ' << (I*)(J*)&y << ' ' << (I*)(K*)&y << '\n'; 

} 

Drucke:

0x600d60 0x600d60 0x600d60 0x600d60 0x600d61 

Für den Typ Y nur eine der I Basen können bei Null ausgeglichen werden, so dass, obwohl die X Subobjekt bei Null-Offset ist (d.h.offsetof(Y, i) ist Null) und eine der I Basen ist an der gleichen Adresse, aber die andere I Basis ist (zumindest mit G ++ und Clang ++) ein Byte in das Objekt, also wenn Sie eine I* haben, können Sie nicht reinterpret_cast zu weil Sie würden nicht wissen, dieI Unter Objekt, um es zugespitzt, die I bei 0 Offset oder die I Offset 1.

es ist in Ordnung für den Compiler die zweite I Unterobjekt setzen bei 1 Offset (dh innerhalb der int) weil I keine nicht statischen Daten Mitglieder hat, so dass Sie nicht acallall Dereferenzieren oder greifen Sie auf irgendetwas an dieser Adresse zu, erhalten Sie nur einen Zeiger auf das Objekt an dieser Adresse. Wenn Sie nicht statische Datenelemente zu I hinzugefügt hätten, wäre Y nicht mehr das Standardlayout und würde nicht den EBO verwenden müssen, und offsetof(Y, i) wäre nicht mehr null.

+0

perfekt, so funktioniert es :) – gexicide

+0

beachten Sie die wichtige Einschränkung, die ich in meinem Edit erweitert, obwohl –

+0

ja, natürlich. Ich muss sicherstellen, dass die Klasse Standard-Layout hat. Vielen Dank! – gexicide