2012-07-03 12 views
7

Ich möchte einen neuen Typ in C++ definieren, der nur ein primitiver Typ ist (in meinem Beispiel ein int, könnte jeder Typ sein). In diesem Beispiel rufe ich den Typ NodeId auf.Leistung: Typedef vs Wrapper-Klasse für primitive Typen?

Ich konnte einfach typedef int NodeId verwenden. Ich möchte einen Standardwert für NodeId s, also würde ich #define NULL_NODE_ID -1 verwenden.

Nun, ich denke, es wäre schöner sein, eine Klasse statt typedef definiert eine Funktion isValid() und ein Default-Konstruktor, damit NodeId ein Null-Konstruktion:

class NodeId 
{ 
    int value; 
public: 
    inline NodeId() : value(-1) {} 
    inline NodeId(int value) : value(value) {} 
    inline operator int() {return value;} 
    inline bool isValid() {return value != -1;} 
    //... 
}; 

Gibt es Performance-Nachteile, die die bei der Verwendung führen zweite Methode?

+0

Wenn Sie einen neuen Compiler verwenden, dann soll, dass das gleiche sein wie die int verwenden. – mfontanini

+0

@mfontanini Warum haben Sie dann die Frage abgelehnt? Dies ist die Antwort auf die Frage, die Sie als solche posten sollten. – leemes

+0

@leemes Warum gehst du davon aus, dass ich es war? – mfontanini

Antwort

6

Tatsächlich gibt es zwei Gründe, dies denkbar langsamer sein könnte.

Zunächst gibt es keine Möglichkeit, eine nicht initialisierte NodeId zu erstellen. Normalerweise ist das eine gute Sache. Aber stellen Sie Code wie dieses:

NodeId nodeid; 
foo.initializeNodeId(&nodeid); 

Sie erhalten eine zusätzliche Zuweisung tun, die nicht wirklich notwendig ist.

Sie können das beheben, indem Sie einen speziellen Konstruktor hinzufügen. Es ist wahrscheinlich viel besser, ein Foo :: createNodeId() zu erstellen, so dass Sie Foo :: initializeNodeId (& NodeId) nicht benötigen, aber wenn Sie die Definition von Foo nicht kontrollieren, ist das möglicherweise nicht möglich.

Zweitens ist eine NodeId kein kompilierbarer Konstantenausdruck. Wie dasblinkenlight andeutet, ist dies viel wahrscheinlicher, dass es Probleme gibt, dass Code nicht legal ist, um Leistungsprobleme zu verursachen, aber beides ist möglich. (Warum? Weil Sie den Compiler zwingt, Code einzufügen, um einige Berechnungen zur Laufzeit durchzuführen, die zur Kompilierungszeit hätten ausgeführt werden können, wenn Sie einen int verwendet hätten. Nicht dass dies ein Problem für eine Klasse namens NodeId wäre ...

) Zum Glück

, wenn Sie mit C 11 ++, können Sie das in Ordnung bringen mit constexpr. Und wenn Ihr Code auch C++ 03 sein soll, können Sie das mit einem Makro erledigen.

Auch, wie durch dasblinkenlight wies darauf hin, sind Sie nicht const in zwei Methoden.

Schließlich gibt es noch keinen Grund, „inline“ auf Methoden zu schreiben, die innerhalb der Klassendefinition definiert sind; Sie sind bereits inhärent inline.

es Putting alles zusammen:

#if __cplusplus > 201000L 
#define CONSTEXPR_ constexpr 
#else 
#define CONSTEXPR_ 
#endif 

class NodeId 
{ 
    int value; 
public: 
    struct Uninitialized {}; 
    CONSTEXPR_ NodeId() : value(-1) {} 
    CONSTEXPR_ NodeId(Uninitialized) {}  
    CONSTEXPR_ NodeId(int value) : value(value) {} 
    CONSTEXPR_ operator int() const {return value;} 
    CONSTEXPR_ bool isValid() const {return value != -1;} 
    //... 
}; 

Jetzt können Sie dies tun, die Kosten für eine zusätzliche -1 Zuordnung zu vermeiden.

NodeId nodeId(NodeId::Uninitialized()); 
foo.initializeNodeId(&nodeid); 

Und dies rechtlich eine NodeId als nicht-Typ Template-Parameter zu verwenden:

myClassTemplate<NodeId(3)> c; 

Oder dies kann der Compiler sicher sein legal nur x bis 4 initialisieren:

int x = 3; 
x += NodeId(1); 
+0

Vielen Dank für die große Antwort :) – Misch

+0

abarnert, gibt es eine Möglichkeit Integer-Arithmetik aus der Box zu unterstützen? 'NodeId Knoten; node ++; 'kompiliert nicht. Ich muss den 'operator ++' manuell implementieren. Ist das möglich, ohne diese trivialen Operatoren manuell zu implementieren? – leemes

+0

@leemes: Nein, nicht komplett automatisch. Aber boost.operator kann Ihnen helfen, einige explizit zu definieren und andere kostenlos zu erhalten. Wenn Sie viele dieser Klassen verwenden, können Sie CRTP, eine Basisklasse oder einen Codegenerator verwenden, sodass Sie alle Operatoren nur einmal definieren müssen. Oder definieren Sie eine einzelne "SmartInt" -Klassenvorlage und dann etwas wie "struct NodeIdTag {}; typedef SmartInt NodeId;" (was man natürlich in ein Makro einpacken kann). – abarnert

4

Wenn Sie einen relativ neuen Compiler verwenden, sollte der generierte Code derselbe sein. Der Compiler sollte keine Probleme haben, diese Member-Funktionen zu umreißen. Falls Sie wirklich Zweifel daran haben, können Sie die ausführbare Datei immer zerlegen.

+0

Das stimmt nicht ganz. 'int' ist ein POD-Typ, aber' NodeId' ist nicht. Der Compiler generiert zusätzlichen Initialisierungscode, wo er verwendet wird, Sie dürfen ihn nicht in Unionen verwenden, und er wird andere Klassen mit seiner Nicht-PODness "infizieren". –

+0

'NodeId x;' sollte den gleichen Code wie „int x (-1),“ produzieren, denn das ist die OP Art und Weise ist diese Art verwenden würde, wenn es ein 'int' ist. Das Nicht-POD-Argument ist jedoch wahr. – mfontanini

+0

@DanHulme C++ 11 [ermöglicht] (https://en.wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions) nicht-Aggregate in Gewerkschaften – Praetorian

3

Wenn die Compiler-Optimierungseinstellungen den Compiler inline alles lassen, sollte es keine Leistungsunterschiede sein. Der einzige Nachteil, den ich feststellen kann, ist, dass Ausdrücke, die auf Objekten dieser Klasse basieren, nicht mehr als Kompilierzeitkonstanten gelten, was bei der Template-Programmierung wichtig sein kann. Ansonsten erhalten Sie zusätzliche Klarheit ohne Laufzeitkosten.


P.S. Sie fehlen const in Ihrem operator int und isValid:

inline operator int() const {return value;} 
inline bool isValid() const {return value != -1;} 
+1

"Kompilierzeit * konstante Ausdrücke *", vielleicht? –

+1

du hast recht über die const, ich habe gerade dieses Beispiel gehackt, um es hier zu posten. – Misch

1

Wenn Sie planen, dies zu einer Bibliothek zu machen, die von einer anderen Programmiersprache verwendet werden kann (wie wenn Sie dies in eine C++ DLL schreiben), gibt es bestimmte Vorteile, es zu haben typedef int.

Zum Beispiel, wenn Sie einen Python-Wrapper oder einen Java-Wrapper für Ihre API zu schreiben, müssen Sie nicht so viele Kopfschmerzen Ihre Klasse und Arten in dem Hafen über zu bekommen. Dann müssen Sie sich nur um die Bitgröße des int kümmern.