Kürzlich habe ich ein Beispiel wie das folgende gesehen:
#include <iostream>
class Foo {
public:
int bar;
Foo(int num): bar(num) {};
};
int main(void) {
std::cout << Foo(42).bar << std::endl;
return 0;
}
Was bedeutet dieses seltsame : bar(num)
? Es scheint irgendwie, die Mitgliedsvariable zu initialisieren, aber ich habe diese Syntax noch nie gesehen. Es sieht aus wie ein Funktions-/Konstruktoraufruf, aber für ein int
? Macht für mich keinen Sinn. Vielleicht könnte mich jemand aufklären. Übrigens, gibt es noch andere esoterische Sprachfunktionen wie diese, die Sie in keinem normalen C++ - Buch finden werden?
Es ist eine Initialisierungsliste für Mitglieder . Informationen dazu finden Sie in jedem gutes C++ - Buch .
In den meisten Fällen sollten Sie alle Elementobjekte in der Elementinitialisierungsliste initialisieren. (Beachten Sie jedoch die Ausnahmen, die am Ende des FAQ Eintrags) aufgeführt sind.).
Der Abholpunkt vom FAQ Eintrag ist, dass,
Wenn alle anderen Faktoren gleich sind, wird Ihr Code schneller ausgeführt, wenn Sie Initialisierungslisten anstelle von Zuweisungen verwenden.
Foo(int num): bar(num)
Dieses Konstrukt wird in C++ als Member Initializer List bezeichnet.
Einfach gesagt, es initialisiert Ihr Mitglied bar
auf einen Wert num
.
Member Initialization:
Foo(int num): bar(num) {};
Mitgliederzuordnung:
Foo(int num)
{
bar = num;
}
Es gibt einen signifikanten Unterschied zwischen dem Initialisieren eines Elements mithilfe der Elementinitialisierungsliste und dem Zuweisen eines Werts innerhalb des Konstruktorkörpers.
Wenn Sie Felder über die Elementinitialisierungsliste initialisieren, werden die Konstruktoren einmal aufgerufen und das Objekt in einer Operation erstellt und initialisiert .
Wenn Sie die Zuweisung verwenden, werden die Felder zuerst mit Standardkonstruktoren initialisiert und dann (über den Zuweisungsoperator) mit tatsächlichen Werten neu zugewiesen .
Wie Sie sehen, entsteht in letzterem ein zusätzlicher Aufwand für Erstellung und Zuweisung, der für benutzerdefinierte Klassen erheblich sein kann.
Cost of Member Initialization = Object Construction
Cost of Member Assignment = Object Construction + Assignment
Letzteres ist eigentlich gleichbedeutend mit:
Foo(int num) : bar() {bar = num;}
Während der erstere ist gleichbedeutend mit nur:
Foo(int num): bar(num){}
Für eingebaute (Ihr Codebeispiel) oder POD-Klassenmitglieder gibt es keinen praktischen Aufwand.
Sie werden müssen (eher gezwungen) eine Member Initializer-Liste verwenden, wenn:
class MyClass
{
public:
//Reference member, has to be Initialized in Member Initializer List
int &i;
int b;
//Non static const member, must be Initialized in Member Initializer List
const int k;
//Constructor’s parameter name b is same as class data member
//Other way is to use this->b to refer to data member
MyClass(int a, int b, int c):i(a),b(b),k(c)
{
//Without Member Initializer
//this->b = b;
}
};
class MyClass2:public MyClass
{
public:
int p;
int q;
MyClass2(int x,int y,int z,int l,int m):MyClass(x,y,z),p(l),q(m)
{
}
};
int main()
{
int x = 10;
int y = 20;
int z = 30;
MyClass obj(x,y,z);
int l = 40;
int m = 50;
MyClass2 obj2(x,y,z,l,m);
return 0;
}
MyClass2
hat keinen Standardkonstruktor und muss daher über die Member-Initialisierungsliste initialisiert werden.MyClass
hat keinen Standardkonstruktor. Um ihr Mitglied zu initialisieren, muss die Mitgliederinitialisierungsliste verwendet werden.Klassenmitgliedervariablen werden immer in der Reihenfolge initialisiert, in der sie in der Klasse deklariert sind.
Sie werden nicht in der Reihenfolge initialisiert, in der sie in der Mitgliederinitialisierungsliste angegeben sind.
Kurz gesagt, die Mitgliederinitialisierungsliste bestimmt nicht die Reihenfolge der Initialisierung.
In Anbetracht der obigen Ausführungen ist es immer ratsam, für die Initialisierung der Mitglieder die gleiche Reihenfolge beizubehalten, in der sie in der Klassendefinition deklariert sind. Dies liegt daran, dass Compiler nicht warnen, wenn sich die beiden Befehle unterscheiden, ein relativ neuer Benutzer jedoch die Initialisierungsliste des Mitglieds als Initialisierungsreihenfolge verwechseln und einen davon abhängigen Code schreiben kann.
Das ist die Konstruktorinitialisierung. Dies ist die richtige Methode zum Initialisieren von Mitgliedern in einem Klassenkonstruktor, da hierdurch das Aufrufen des Standardkonstruktors verhindert wird.
Betrachten Sie diese beiden Beispiele:
// Example 1
Foo(Bar b)
{
bar = b;
}
// Example 2
Foo(Bar b)
: bar(b)
{
}
In Beispiel 1:
Bar bar; // default constructor
bar = b; // assignment
In Beispiel 2:
Bar bar(b) // copy constructor
Alles dreht sich um Effizienz.
Dies wird als Initialisierungsliste bezeichnet. Dies ist eine Methode zum Initialisieren von Klassenmitgliedern. Es hat Vorteile, dies zu verwenden, anstatt den Elementen im Rumpf des Konstruktors einfach neue Werte zuzuweisen. Wenn Sie jedoch Klassenelemente haben, die Konstanten oder Referenzen sie - muss initialisiert werden.
Dies ist nicht unklar, es ist die C++ - Initialisierungslistensyntax
Grundsätzlich wird in Ihrem Fall x
mit _x
, y
mit _y
, z
mit _z
Initialisiert. .
Die andere hat Ihnen bereits erklärt, dass die Syntax, die Sie einhalten, "Konstruktorinitialisiererliste" heißt. Mit dieser Syntax können Sie Basis-Unterobjekte und Element-Unterobjekte der Klasse benutzerdefiniert initialisieren (anstatt zuzulassen, dass sie standardmäßig initialisiert werden oder nicht initialisiert werden).
Ich möchte nur bemerken, dass die Syntax, die, wie Sie sagten, "wie ein Konstruktoraufruf aussieht", nicht unbedingt ein Konstruktoraufruf ist. In C++ Sprache ist das ()
Syntax ist nur eine Standardform von Initialisierungssyntax. Es wird für verschiedene Typen unterschiedlich interpretiert. Für Klassentypen mit benutzerdefiniertem Konstruktor bedeutet dies eine Sache (es ist in der Tat ein Konstruktoraufruf), für Klassentypen ohne benutzerdefinierten Konstruktor bedeutet dies eine andere Sache (so genanntes = Wertinitialisierung ) für leeres ()
) und für Nichtklassentypen bedeutet dies wiederum etwas anderes (da Nichtklassentypen keine Konstruktoren haben).
In Ihrem Fall hat das Datenelement den Typ int
. int
ist kein Klassentyp, hat also keinen Konstruktor. Für den Typ int
bedeutet diese Syntax einfach "bar
mit dem Wert num
initialisieren" und das war's. Es wird einfach so gemacht, direkt, ohne dass Konstruktoren involviert sind, da int
wiederum kein Klassentyp ist und daher keine Konstruktoren haben kann.
Ich weiß nicht, wie du das hier vermissen könntest, es ist ziemlich einfach. Dies ist die Syntax zum Initialisieren von Membervariablen oder Basisklassenkonstruktoren. Es funktioniert sowohl für einfache alte Datentypen als auch für Klassenobjekte.
Dies ist eine Initialisierungsliste. Die Member werden initialisiert, bevor der Konstruktortext ausgeführt wird. Erwägen
class Foo {
public:
string str;
Foo(string &p)
{
str = p;
};
};
vs
class Foo {
public:
string str;
Foo(string &p): str(p) {};
};
Im ersten Beispiel wird str von seinem Konstruktor ohne Argumente initialisiert
string();
vor dem Körper des Foo-Konstruktors. Im foo Konstruktor wird der
string& operator=( const string& s );
wird bei 'str' aufgerufen, wie Sie es tun str = p;
Im zweiten Beispiel wird str direkt durch Aufrufen seines Konstruktors initialisiert
string( const string& s );
mit 'p' als Argument.
Es ist eine Initialisierungsliste für den Konstruktor. Anstatt standardmäßig x
, y
und z
zu konstruieren und ihnen dann die in den Parametern empfangenen Werte zuzuweisen, werden diese Elemente von Anfang an mit diesen Werten initialisiert. Dies mag für float
s nicht sonderlich nützlich erscheinen, kann jedoch bei benutzerdefinierten Klassen, deren Konstruktion teuer ist, eine ziemliche Zeitersparnis bedeuten.
Sie haben Recht, dies ist in der Tat eine Möglichkeit, Mitgliedsvariablen zu initialisieren. Ich bin mir nicht sicher, ob dies von großem Nutzen ist, abgesehen davon, dass es sich eindeutig um eine Initialisierung handelt. Ein "bar = num" im Code kann viel leichter verschoben, gelöscht oder falsch interpretiert werden.
es gibt noch einen weiteren "Vorteil"
wenn der Elementvariablentyp keine Nullinitialisierung unterstützt oder eine Referenz ist (die nicht nullinitialisiert werden kann), müssen Sie eine Initialisierungsliste angeben
In diesem Thread noch nicht erwähnt: Seit C++ 11 kann die Liste der Member-Initialisierer die Listeninitialisierung verwenden (auch bekannt als "einheitliche Initialisierung", "geschweifte Initialisierung"):
Foo(int num): bar{num} {}
dies hat die gleiche Semantik wie die Listeninitialisierung in anderen Kontexten.