Antworten:
Angenommen, diese Werte sind primitive Typen, dann gibt es keinen Unterschied. Initialisierungslisten machen nur dann einen Unterschied, wenn Sie Objekte als Mitglieder haben, da Sie anstelle der Standardinitialisierung mit anschließender Zuweisung in der Initialisierungsliste das Objekt auf seinen endgültigen Wert initialisieren können. Dies kann tatsächlich spürbar schneller sein.
Sie müssen die Initialisierungsliste verwenden, um konstante Mitglieder, Referenzen und Basisklassen zu initialisieren
Wenn Sie konstante Elemente, Referenzen und Parameter an Basisklassenkonstruktoren initialisieren müssen, wie in den Kommentaren erwähnt, müssen Sie die Initialisierungsliste verwenden.
struct aa
{
int i;
const int ci; // constant member
aa() : i(0) {} // will fail, constant member not initialized
};
struct aa
{
int i;
const int ci;
aa() : i(0) { ci = 3;} // will fail, ci is constant
};
struct aa
{
int i;
const int ci;
aa() : i(0), ci(3) {} // works
};
Beispiel (nicht erschöpfend) Klasse / Struktur enthält Referenz:
struct bb {};
struct aa
{
bb& rb;
aa(bb& b ) : rb(b) {}
};
// usage:
bb b;
aa a(b);
Ein Beispiel für die Initialisierung einer Basisklasse, für die ein Parameter erforderlich ist (z. B. kein Standardkonstruktor):
struct bb {};
struct dd
{
char c;
dd(char x) : c(x) {}
};
struct aa : dd
{
bb& rb;
aa(bb& b ) : dd('a'), rb(b) {}
};
_capacity
, _data
und _len
haben Klassentypen ohne zugängliche Standardkonstruktoren?
const
im Konstruktorkörper nicht initialisieren können. Sie müssen die Initialisierungsliste verwenden. Nicht- const
Mitglieder können in der Initialisierungsliste oder im Konstruktorkörper initialisiert werden.
Ja. Im ersten Fall können Sie erklären _capacity
, _data
und _len
als Konstanten:
class MyClass
{
private:
const int _capacity;
const void *_data;
const int _len;
// ...
};
Dies ist wichtig, wenn Sie die const
Aktualität dieser Instanzvariablen sicherstellen möchten, während Sie ihre Werte zur Laufzeit berechnen. Beispiel:
MyClass::MyClass() :
_capacity(someMethod()),
_data(someOtherMethod()),
_len(yetAnotherMethod())
{
}
const
Instanzen müssen in der Initialisierungsliste initialisiert werden, oder die zugrunde liegenden Typen müssen öffentliche parameterlose Konstruktoren bereitstellen (was primitive Typen tun).
Ich denke, dieser Link http://www.cplusplus.com/forum/articles/17820/ bietet eine hervorragende Erklärung - insbesondere für diejenigen, die neu in C ++ sind.
Der Grund, warum Intialiser-Listen effizienter sind, besteht darin, dass innerhalb des Konstruktorkörpers nur Zuweisungen stattfinden, keine Initialisierung. Wenn Sie also mit einem nicht integrierten Typ arbeiten, wurde der Standardkonstruktor für dieses Objekt bereits aufgerufen, bevor der Hauptteil des Konstruktors eingegeben wurde. Innerhalb des Konstruktorkörpers weisen Sie diesem Objekt einen Wert zu.
Tatsächlich ist dies ein Aufruf des Standardkonstruktors, gefolgt von einem Aufruf des Kopierzuweisungsoperators. Mit der Initialisiererliste können Sie den Kopierkonstruktor direkt aufrufen. Dies kann manchmal erheblich schneller sein (denken Sie daran, dass sich die Initialisiererliste vor dem Hauptteil des Konstruktors befindet).
Ich füge hinzu, dass die Initialisierung die einzige Möglichkeit ist, Ihre Klasse zu erstellen, wenn Sie Mitglieder vom Klassentyp haben, für die kein Standardkonstruktor verfügbar ist.
Ein großer Unterschied besteht darin, dass die Zuweisung Mitglieder einer übergeordneten Klasse initialisieren kann. Der Initialisierer funktioniert nur mit Mitgliedern, die im aktuellen Klassenbereich deklariert sind.
Hängt von den beteiligten Typen ab. Der Unterschied ist ähnlich zwischen
std::string a;
a = "hai";
und
std::string a("hai");
Wenn die zweite Form die Initialisierungsliste ist, macht es einen Unterschied, ob der Typ Konstruktorargumente erfordert oder mit Konstruktorargumenten effizienter ist.
Der wahre Unterschied besteht darin, wie der gcc-Compiler Maschinencode generiert und den Speicher festlegt. Erklären:
Es gibt sicherlich andere Möglichkeiten, mit Mitgliedern vom Typ const umzugehen. Um ihnen das Leben zu erleichtern, beschließen die Autoren des gcc-Compilers, einige Regeln aufzustellen
Es gibt nur eine Möglichkeit, Basisklasseninstanzen und nicht statische Elementvariablen zu initialisieren , nämlich die Initialisierungsliste.
Wenn Sie in der Initialisierungsliste Ihres Konstruktors keine Basis- oder nicht statische Elementvariable angeben, wird dieses Element oder diese Basis entweder standardmäßig initialisiert (wenn das Element / die Basis ein Nicht-POD-Klassentyp oder ein Array einer Nicht-POD-Klasse ist Typen) oder anderweitig nicht initialisiert.
Sobald der Konstruktorkörper eingegeben wurde, wurden alle Basen oder Mitglieder initialisiert oder nicht initialisiert (dh sie haben einen unbestimmten Wert). Im Konstruktorkörper gibt es keine Möglichkeit zu beeinflussen, wie sie initialisiert werden sollen.
Möglicherweise können Sie Mitgliedern im Konstruktorkörper neue Werte zuweisen, aber es ist nicht möglich, const
Mitgliedern oder Mitgliedern des Klassentyps zuzuweisen, die nicht zuweisbar gemacht wurden, und es ist nicht möglich, Referenzen erneut zu binden.
Bei integrierten Typen und einigen benutzerdefinierten Typen kann die Zuweisung im Konstruktorkörper genau den gleichen Effekt haben wie die Initialisierung mit demselben Wert in der Initialisierungsliste.
Wenn Sie ein Mitglied oder eine Basis in einer Initialisierungsliste nicht benennen und diese Entität eine Referenz ist, einen Klassentyp ohne vom Benutzer deklarierten Standardkonstruktor hat, const
qualifiziert ist und einen POD-Typ hat oder ein POD-Klassentyp oder ein Array vom POD-Klassentyp ist Wenn Sie ein const
qualifiziertes Mitglied (direkt oder indirekt) enthalten, ist das Programm schlecht ausgebildet.
Wenn Sie eine Initialisierungsliste schreiben, erledigen Sie alles in einem Schritt. Wenn Sie keine Initilizer-Liste schreiben, führen Sie zwei Schritte aus: einen für die Deklaration und einen für die Zuweisung des Werts.
Es gibt einen Unterschied zwischen der Initialisierungsliste und der Initialisierungsanweisung in einem Konstruktor. Betrachten wir den folgenden Code:
#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>
class MyBase {
public:
MyBase() {
std::cout << __FUNCTION__ << std::endl;
}
};
class MyClass : public MyBase {
public:
MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
std::cout << __FUNCTION__ << std::endl;
}
private:
int _capacity;
int* _data;
int _len;
};
class MyClass2 : public MyBase {
public:
MyClass2::MyClass2() {
std::cout << __FUNCTION__ << std::endl;
_capacity = 15;
_data = NULL;
_len = 0;
}
private:
int _capacity;
int* _data;
int _len;
};
int main() {
MyClass c;
MyClass2 d;
return 0;
}
Wenn MyClass verwendet wird, werden alle Mitglieder vor der ersten Anweisung in einem ausgeführten Konstruktor initialisiert.
Wenn MyClass2 verwendet wird, werden jedoch nicht alle Mitglieder initialisiert, wenn die erste Anweisung in einem Konstruktor ausgeführt wird.
In einem späteren Fall kann es zu einem Regressionsproblem kommen, wenn jemand einem Konstruktor Code hinzufügt, bevor ein bestimmtes Mitglied initialisiert wird.
Hier ist ein Punkt, den andere nicht erwähnt haben:
class temp{
public:
temp(int var);
};
Die temporäre Klasse hat keinen Standard-Ctor. Wenn wir es in einer anderen Klasse wie folgt verwenden:
class mainClass{
public:
mainClass(){}
private:
int a;
temp obj;
};
Der Code wird nicht kompiliert, da der Compiler nicht weiß, wie er initialisiert werden soll obj
, weil er nur einen expliziten ctor hat, der einen int-Wert empfängt. Daher müssen wir den ctor wie folgt ändern:
mainClass(int sth):obj(sth){}
Es geht also nicht nur um const und Referenzen!