Was ist diese seltsame Doppelpunkt-Member-Syntax (” : “) im Konstruktor?

Lesezeit: 9 Minuten

Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
Nullen

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 das seltsam : bar(num) bedeuten? Es scheint irgendwie die Member-Variable zu initialisieren, aber ich habe diese Syntax noch nie zuvor gesehen. Es sieht aus wie ein Funktions-/Konstruktoraufruf, aber für eine int? Macht für mich keinen Sinn. Vielleicht könnte mich jemand aufklären. Und übrigens, gibt es noch andere esoterische Sprachfeatures wie diese, die Sie niemals in einem gewöhnlichen C++-Buch finden werden?

  • Ein “normales C++-Buch”, das dies nicht erwähnt, ist wahrscheinlich ein AC-Buch, bei dem jemand dachte, “++” würde auf dem Cover cool aussehen …

    – Rasmus Kaj

    10. November 2009 um 23:35 Uhr

  • “Sie werden es nie in einem gewöhnlichen C++-Buch finden”. Oh. Sehr geehrter. Werfen Sie Ihr „gewöhnliches C++-Buch“ sofort weg. Nicht aus dem Fenster – jemand anderes könnte es aufheben. Am besten zerkleinern und dem Recycling zuführen. Getan? Konsultieren Sie jetzt stackoverflow.com/questions/388242/…, um ein neues Buch zu erhalten.

    – Steve Jessop

    10. November 2009 um 23:36 Uhr

  • Dieses Sprachmerkmal ist kaum esoterisch. Es ist ein ziemlich wichtiges Merkmal der Objektkonstruktion.

    – Karl Salvia

    10. November 2009 um 23:36 Uhr

  • In der Tat, alles andere als esoterisch, haben Sie oft keine andere Wahl, als Initialisierungslisten zu verwenden. Wenn Ihre Klasse beispielsweise eine enthält const Mitgliedsvariable oder eine Referenz, müssen Sie eine Initialisierungsliste verwenden.

    – Karl Salvia

    10. November 2009 um 23:42 Uhr

Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
Alok Speichern

Foo(int num): bar(num)    

Dieses Konstrukt heißt a Mitgliedsinitialisiererliste in C++.

Einfach gesagt, es initialisiert Ihr Mitglied bar zu einem Wert num.


Was ist der Unterschied zwischen Initialisierung und Zuweisung innerhalb eines Konstruktors?

Member-Initialisierung:

Foo(int num): bar(num) {};

Mitgliedszuweisung:

Foo(int num)
{
   bar = num;
}

Es besteht ein wesentlicher Unterschied zwischen dem Initialisieren eines Members mithilfe der Member-Initialisierungsliste und dem Zuweisen eines Werts innerhalb des Konstruktorkörpers.

Wenn du initialisieren Felder über die Member-Initialisierungsliste werden die Konstruktoren einmal aufgerufen und das Objekt wird in einer Operation konstruiert und initialisiert.

Wenn du benutzt Abtretung dann werden die Felder zuerst mit Standardkonstruktoren initialisiert und dann (über den Zuweisungsoperator) mit tatsächlichen Werten neu zugewiesen.

Wie Sie sehen, gibt es in letzterem einen zusätzlichen Aufwand für die 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 äquivalent zu:

Foo(int num) : bar() {bar = num;}

Während ersteres gleichbedeutend ist mit:

Foo(int num): bar(num){}

Für einen eingebauten (Ihr Codebeispiel) oder POD-Klassenmember gibt es keinen praktischen Overhead.


Wann MÜSSEN Sie die Member Initializer-Liste verwenden?

Du wirst haben (eher gezwungen). Verwenden Sie eine Member-Initialisierungsliste, wenn:

  • Ihre Klasse hat ein Referenzelement
  • Ihre Klasse hat ein nicht statisches Konstantenmitglied oder
  • Ihr Klassenmitglied hat keinen Standardkonstruktor oder
  • Zur Initialisierung von Basisklassenmitgliedern oder
  • Wenn der Parametername des Konstruktors mit dem Datenelement identisch ist (dies ist nicht wirklich ein MUSS)

Ein Codebeispiel:

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, daher muss er über die Member-Initialisierungsliste initialisiert werden.
  • Basisklasse MyClass hat keinen Standardkonstruktor. Um sein Mitglied zu initialisieren, muss also die Member Initializer List verwendet werden.

Wichtige Punkte, die bei der Verwendung von Member Initializer Lists zu beachten sind:

Klassenmember-Variablen werden immer in der Reihenfolge initialisiert, in der sie in der Klasse deklariert werden.

Sie sind nicht in der Reihenfolge initialisiert, in der sie in der Member-Initialisierungsliste angegeben sind.
Kurz gesagt, die Member-Initialisierungsliste bestimmt nicht die Reihenfolge der Initialisierung.

In Anbetracht des Obigen ist es immer eine gute Praxis, die gleiche Reihenfolge der Member für die Member-Initialisierung beizubehalten wie die Reihenfolge, in der sie in der Klassendefinition deklariert sind. Dies liegt daran, dass Compiler nicht warnen, wenn die beiden Reihenfolgen unterschiedlich sind, aber ein relativ neuer Benutzer könnte die Member-Initialisiererliste mit der Reihenfolge der Initialisierung verwechseln und davon abhängigen Code schreiben.

  • @nils Das ist die bisher beste Antwort. Die Reihenfolge der Initialisierung, auf die Als hingewiesen wurde, ist ebenfalls äußerst wichtig, während der Visual Studio-Compiler nichts darüber sagt, andere Compiler wie gcc werden fehlschlagen. Es ist auch wichtig, je nach Compiler und Situation zu beachten nicht immer wahr dass dies die Leistung verbessert oder effizienter ist.

    – ForceMagic

    14. März 2012 um 7:21 Uhr

  • @ryf9059: Warum denkst du, dass es unpraktisch sein wird? Sie müssen sie sowieso auflisten, warum also nicht in der gleichen Reihenfolge wie bei der Deklaration?

    – Alok Speichern

    7. Januar 2013 um 12:10 Uhr

  • das hätte die Antwort sein sollen. Gott sei Dank habe ich nach unten gescrollt, sonst hätte ich es übersehen.

    – Kaffeeliebhaber

    13. August 2015 um 11:41 Uhr

  • @AlokSave MeineKlasse(int a, int b, int c) : i(a), b(b), k(c) { // Ohne Member-Initialisierer // this->b = b; } es sollte so sein MeineKlasse (int &a int b, int c) : i(a), b(b), k(c) { // Ohne Member-Initialisierer // this->b = b; } und damit entsprechende Änderungen in Deklaration und Aufruf. ohne diese Änderung i wird darauf verweisen a aber a kann nicht darauf verweisen x da es nur den Wert von enthält x also indirekt i kann nicht darauf verweisen x. Wenn wir also den Wert von ändern i dann ändere es einfach a aber nicht x

    – Abhishek Mähne

    15. Mai 2021 um 11:42 Uhr


  • @AbhishekMane Sie haben Recht, und hier ist ein Link zu der zugehörigen Frage, die dies zeigt: stackoverflow.com/q/67619383/3150802

    – Peter – Setzen Sie Monica wieder ein

    20. Mai 2021 um 12:08 Uhr

1647306612 265 Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
James McNellis

Es ist ein Member-Initialisierungsliste. Informationen darüber sollten Sie in jedem guten C++-Buch finden.

In den meisten Fällen sollten Sie alle Member-Objekte in der Member-Initialisierungsliste initialisieren (beachten Sie jedoch die am Ende des FAQ-Eintrags aufgeführten Ausnahmen).

Der Takeaway-Punkt aus dem FAQ-Eintrag ist, dass

Wenn alle anderen Dinge gleich sind, wird Ihr Code schneller ausgeführt, wenn Sie Initialisierungslisten anstelle von Zuweisungen verwenden.

  • Die Kenntnis der Terminologie ist entscheidend – ich bin eifersüchtig, dass ich nicht daran gedacht habe.

    – Markieren Sie Lösegeld

    10. November 2009 um 23:36 Uhr

  • Es gibt auch eine Reihe anderer Gründe für die Verwendung von Init-Listen. besonders wenn die Reihenfolge der Initialisierung wichtig ist. Es ist eine Schande, dass es eine so dumme gefälschte Funktionsaufrufsyntax hat.

    – Martin Beckett

    10. November 2009 um 23:50 Uhr

  • @mgb, die Initialisierungsliste bestimmt nicht die Reihenfolge der Initialisierung. Member-Variablen werden in der Reihenfolge initialisiert, in der sie in der Klasse deklariert sind, auch wenn sich diese von der Reihenfolge der Initialisierungen im Konstruktor unterscheidet.

    – ScottJ

    11. November 2009 um 0:26 Uhr

  • @mgb: Ich glaube nicht, dass es als gefälschte Syntax für Funktionsaufrufe gedacht ist. Es ist Initialisierungssyntax, wie int i(23);, std::vector<double> emptyVec(0);, std::vector<double> fullVec(10,23.);usw. Natürlich nur mit entferntem Typ, da der Typ in der Elementdeklaration steht.

    – Steve Jessop

    11. November 2009 um 1:03 Uhr

  • @Martin: Es hat keine Funktionsaufrufsyntax, es hat eine Konstruktionssyntax (ala: new String (“Name”)). Es passt besser zum Konstruktor als Foo(int num) : m_Count = 5. Ganz zu schweigen davon, dass an dieser Stelle sowieso Klassen konstruiert werden müssen, da es hier initialisiert wird. Foo(int num) : Bar = num, würde nicht richtig kompilieren. Es scheint nur seltsam zu sein, Foo(int num) : m_Count(num) zu sehen, da primitive Typen nicht konstruiert werden.

    – Lee Louviere

    22. April 2011 um 20:33 Uhr


1647306612 735 Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
Josch

Das ist die Konstruktorinitialisierung. Es ist der richtige Weg, Member in einem Klassenkonstruktor zu initialisieren, da es verhindert, dass der Standardkonstruktor aufgerufen 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

Es geht um Effizienz.

  • Ich würde nicht sagen, dass es um Effizienz geht. Es geht darum, eine Möglichkeit bereitzustellen, etwas zu initialisieren, das eine Initialisierung erfordert, aber nicht standardmäßig initialisiert werden kann. Aus irgendeinem Grund erwähnen die Leute Konstanten und Referenzen als Beispiele, während das offensichtlichste Beispiel Klassen ohne Standardkonstruktoren wären.

    – Ant

    11. November 2009 um 0:26 Uhr

  • Wir haben beide recht; in seinem Beispiel könnte man für Effizienz argumentieren; Für das Problem const/reference/no default Konstruktor ist es sowohl Effizienz als auch Notwendigkeit. Ich habe deswegen eine Antwort unten hochgestuft 🙂 [Farnsworth voice] Es kann andere Dinge tun. Warum sollte es nicht?

    – Josch

    11. November 2009 um 4:19 Uhr

  • Bar bar(); // default constructor Bist du sicher?

    – Leichtigkeitsrennen im Orbit

    27. Dezember 2015 um 14:55 Uhr

  • @LightnessRacesinOrbit Nur neugierig zu wissen: Was soll das deiner Meinung nach sein?

    – ajaysinghnegi

    2. August 2019 um 11:01 Uhr

Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
LeopardSkinPillBoxHat

Dies wird als Initialisierungsliste bezeichnet. Es ist eine Möglichkeit, Klassenmitglieder zu initialisieren. Es hat Vorteile, dies zu verwenden, anstatt den Mitgliedern im Hauptteil des Konstruktors einfach neue Werte zuzuweisen, aber wenn Sie Klassenmitglieder haben, die vorhanden sind Konstanten oder Verweise Sie Muss initialisiert werden.

1647306613 282 Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
逆さま

Das ist nicht obskur, es ist das Syntax der C++-Initialisierungsliste

Grundsätzlich gilt in Ihrem Fall x wird mit initialisiert _x, y mit _y, z mit _z.

1647306614 292 Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
Destruktor

Der andere hat Ihnen bereits erklärt, dass die von Ihnen beobachtete Syntax “Konstruktorinitialisiererliste” heißt. Mit dieser Syntax können Sie Basis-Unterobjekte und Mitglieds-Unterobjekte der Klasse benutzerdefiniert initialisieren (im Gegensatz dazu, dass sie standardmäßig initialisiert werden oder nicht initialisiert bleiben).

Ich möchte nur darauf hinweisen, dass die Syntax, die, wie Sie sagten, “wie ein Konstruktoraufruf aussieht”, nicht unbedingt ein Konstruktoraufruf ist. In der Sprache C++ ist die () Syntax ist nur eine Standardform von Initialisierungssyntax. Es wird für verschiedene Typen unterschiedlich interpretiert. Für Klassentypen mit benutzerdefiniertem Konstruktor bedeutet es eine Sache (es ist tatsächlich ein Konstruktoraufruf), für Klassentypen ohne benutzerdefinierten Konstruktor bedeutet es etwas anderes (sog Wertinitialisierung ) für leer ()) und für Nicht-Klassen-Typen bedeutet es wieder etwas anderes (da Nicht-Klassen-Typen keine Konstruktoren haben).

In Ihrem Fall hat das Datenelement Typ int. int ist kein Klassentyp, hat also keinen Konstruktor. Für Typ int Diese Syntax bedeutet einfach “initialisieren bar mit dem Wert von num” und das war’s. Es wird einfach so gemacht, direkt, keine Konstrukteure beteiligt, denn noch einmal, int ist kein Klassentyp und kann daher keine Konstruktoren haben.

1647306614 531 Was ist diese seltsame Doppelpunkt Member Syntax im Konstruktor
Mark Lösegeld

Ich weiß nicht, wie man das übersehen könnte, es ist ziemlich einfach. Das ist die Syntax zum Initialisieren von Mitgliedsvariablen oder Basisklassenkonstruktoren. Es funktioniert sowohl für einfache alte Datentypen als auch für Klassenobjekte.

  • So in eine Zeile innerhalb der Deklaration geschrieben, ist es leicht, sie nicht als Init-Liste zu erkennen

    – Martin Beckett

    10. November 2009 um 23:51 Uhr

1003540cookie-checkWas ist diese seltsame Doppelpunkt-Member-Syntax (” : “) im Konstruktor?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy