16

Ich lese über Empty Base Optimization (EBO). Beim Lesen tauchte die folgenden Fragen in meinem Kopf nach oben:Wann verwenden Programmierer Empty Base Optimization (EBO)

  1. Was den Punkt der Verwendung von leeren Klasse als Basisklasse ist, wenn es nichts zu den abgeleiteten Klassen trägt (weder Funktionalität weise noch datenweisen)?

  2. In this article, las ich diese:

// S ist leer
Klasse struct T: S
{
            int x;
};

[...]

Beachten Sie, dass wir keine Daten oder Code Genauigkeit verloren haben: wenn Sie ein eigenständiges Objekt vom Typ S erstellen, die Größe des Objekts ist nach wie vor 1 (oder mehr) als vorher; nur wenn S als Basis verwendet wird Klasse einer anderen Klasse seinen Speicher Fußabdruck schrumpfen auf Null. Um zu realisieren, die Auswirkungen dieser Einsparungen, stellen Sie sich einen Vektor vor, der 125.000 Objekte enthält. Das EBO allein spart ein halbes Megabyte Speicher!

Bedeutet es, dass, wenn wir verwenden „S“ als Basisklasse „T“ nicht, wir würden notwendigerweise verbrauchen doppelt so Megabyte Speicher? Ich denke, der Artikel vergleicht zwei verschiedene Szenarien, die ich nicht für richtig halte.

Ich würde gerne ein echtes Szenario kennen, wenn EBO sich als nützlich erweisen könnte (bedeutet, in dem gleichen Szenario würden wir notwendigerweise verlieren, wenn wir EBO nicht verwenden!).

Bitte beachten Sie, dass, wenn Ihre Antwort Erklärungen wie diese enthält:

Der springende Punkt ist, dass eine leere Klasse nicht-Größe Null hat, aber wenn abgeleitet oder Ableiten es die Größe Null haben kann, dann bin ich nicht zu fragen das, wie ich das schon weiß. Meine Frage ist, warum sollte jemand seine Klasse überhaupt von einer leeren Klasse ableiten? Auch wenn er seine Klasse nicht ableitet und einfach schreibt (ohne irgendeine leere Basis), ist er in irgendeiner Weise verloren?

+0

@Suma ... was hast du in meinem Beitrag geändert? : -/.. Ich bin nicht in der Lage, herauszufinden .. – Nawaz

+0

Warum sagen Sie, dass die Basisklasse nichts funktional beiträgt? – jalf

+0

@jalf .... bisher habe ich kein Beispiel gesehen ... Übrigens, mit Funktionalität meinte ich Funktionen, die etwas tun, anstatt nichts – Nawaz

Antwort

28

EBO ist wichtig im Zusammenhang mit policy based design, wo Sie normalerweise privat aus mehreren Richtlinienklassen erben.Wenn wir das Beispiel einer Fadensicherheitspolitik nehmen, könnte man den Pseudo-Code vorstellen:

class MTSafePolicy 
{ 
public: 
    void lock() { mutex_.lock(); } 
    void unlock() { mutex_.unlock(); } 

private: 
    Mutex mutex_; 
}; 

class MTUnsafePolicy 
{ 
public: 
    void lock() { /* no-op */ } 
    void unlock() { /* no-op */ } 
}; 

Bei einer Politik based-Design-Klasse wie zum Beispiel:

template<class ThreadSafetyPolicy> 
class Test : ThreadSafetyPolicy 
{ 
    /* ... */ 
}; 

Verwenden der Klasse mit einem MTUnsafePolicy einfach fügen Sie keine Größe Overhead die Klasse Test: es ist ein perfektes Beispiel für nicht für das bezahlen, was Sie nicht verwenden.

+0

@icecrime .... danke für dieses anwesome Beispiel ... +1 von mir .. – Nawaz

6

EBO ist nicht wirklich eine Optimierung (zumindest nicht eine, die Sie im Code tun). Der springende Punkt ist, dass eine leere Klasse eine Größe ungleich Null hat, aber wenn sie abgeleitet oder abgeleitet wird, kann sie null Größe haben.

Dies ist das übliche Ergebnis:

class A { }; 
class B { }; 

class C { }; 
class D : C { }; 

#include <iostream> 
using namespace std; 

int main() 
{ 
     cout << "sizeof(A) + sizeof(B) == " << sizeof(A)+sizeof(B) << endl; 
     cout << "sizeof(D) == " << sizeof(D) << endl; 

     return 0; 
} 

Ausgang:

sizeof(A) + sizeof(B) == 2 
sizeof(D) == 1 

Zum Bearbeiten: Die Optimierung ist, dass, wenn Sie tatsächlich von einem Funktors tun ableiten (zum Beispiel, oder von einer Klasse, die nur statische Mitglieder hat), wird die Größe Ihrer Klasse (die abgeleitet wird) nicht um 1 erhöht (oder wahrscheinlicher 4 oder 8 aufgrund von Füllbytes).

+0

@Let_Me_Be ... Ich habe meine Frage bearbeitet ... bitte sehen Sie es noch einmal ... Ich hoffe, Sie werden jetzt verstehen, wonach ich frage. :-) – Nawaz

+0

@Nawaz Ich habe meine Antwort bearbeitet. –

+0

@Let_Me_Be ... danke für dieses Update .. das macht jetzt Sinn..ich suche mehr Antwort ... mit mehr Szenarien .. – Nawaz

0

EBO ist nichts, was der Programmierer beeinflusst, und/oder der Programmierer würde bestraft werden, wenn er nicht aus einer leeren Basisklasse ableitet.

Der Compiler steuert, ob für:

class X : emptyBase { int X; }; 
class Y { int x }; 

erhalten Sie sizeof(X) == sizeof(Y) oder nicht. Wenn Sie dies tun, implementiert der Compiler EBO, wenn nicht, tut es das nicht.

Es gibt nie eine Situation, in der sizeof(Y) > sizeof(X) auftreten würde.

0

Meistens wird eine leere Basisklasse entweder polymorph (was der Artikel erwähnt), als "tag" -Klasse oder als Ausnahmeklasse verwendet (obwohl diese normalerweise von std :: exception abgeleitet ist, die nicht leer ist)). Manchmal gibt es einen guten Grund, eine Klassenhierarchie zu entwickeln, die mit einer leeren Basisklasse beginnt.

Boost.CompressedPair verwendet das EBO, um die Größe von Objekten zu verkleinern, wenn eines der Elemente leer ist.

+0

keine Ahnung, warum das war downvoted, scheint mir gut. –

1

EASTL eine gute Erklärung hat, warum sie EBO benötigt, seine erklärte auch eingehende im Papier sie/Kredit Link

+0

... der Artikel ist zu groß ... Ich werde es lesen, wenn ich genug Zeit habe ... dann werde ich es kommentieren ...: -) ... danke für den Link übrigens. – Nawaz

-1

Der primäre Vorteil, den ich von dynamic_cast ist zu denken. Sie können einen Zeiger auf S setzen und versuchen, ihn zu dynamisieren, was von S erbt - vorausgesetzt, dass S eine virtuelle Funktion wie einen virtuellen Destruktor anbietet, was er ziemlich genau als Basisklasse tun muss. Wenn Sie beispielsweise eine dynamisch typisierte Sprache implementieren, können Sie wünschen oder müssen, dass jeder Typ von einer Basisklasse ausschließlich für den Typ gelöschten Speicher abgeleitet wird und eine Überprüfung über dynamic_cast durchführt.

+1

.. wenn S virtuelle Funktionen hat .. dann ist es wahrscheinlich keine leere Klasse! – Nawaz

+0

@Nawez: Warum nicht? Die Basisklasse S könnte sehr gut No-Op-Implementierungen oder sogar rein virtuell bieten. Schau dir die Antwort von icecrime an. wohl "lock" hätte virtuell sein sollen. – MSalters

+1

@MSalters ... wenn eine Klasse virtuelle Funktionen hat, bedeutet das, dass ihre Größe nicht Null sein kann (zumindest für echte Compiler). Und was die Antwort von icecrime betrifft, würde eine nicht virtuelle Sperre perfekt funktionieren! – Nawaz

4

Die "Optimierung" im EBO bedeutet, dass der Fall, in dem Sie die Basisklasse verwenden, so optimiert werden kann, dass weniger Speicher belegt wird, als wenn Sie ein Element desselben Typs verwenden. I.e. Sie vergleichen

struct T : S 
{ 
     int x; 
}; 

mit

struct T 
{ 
     S s; 
     int x; 
}; 

nicht mit

struct T 
{ 
     int x; 
}; 

Wenn Ihre Frage ist, warum würden Sie eine leere Klasse überhaupt haben (entweder als Mitglied oder als Base) Das liegt daran, dass Sie seine Mitgliedsfunktionen verwenden. Leer bedeutet, dass es kein Datenelement hat, nicht dass es überhaupt keine Mitglieder hat. Solche Dinge werden oft beim Programmieren mit Templates gemacht, wo die Basisklasse manchmal "leer" ist (keine Datenmitglieder) und manchmal nicht.

4

Sein verwendet, wenn Programmierer wollen aussetzen einige Daten zum Client, ohne die Clientklassengröße zu erhöhen. Die leere Klasse kann enums und typedefs enthalten oder einige Definitionen, die der Client verwenden kann. Die vernünftigste Art, eine solche Klasse zu verwenden, erbt eine solche Klasse privat. Dadurch werden die Daten von außen ausgeblendet und die Klassengröße wird nicht erhöht.