2009-04-21 5 views
8

Lassen Sie uns sagen, dass ich eine Struktur haben wie folgt definiert:Was bedeutet es, ein Objekt mit geschweiften Klammern in C++ zu instanziieren?

typedef 
struct number{ 
    int areaCode; 
    int prefix; 
    int suffix; 
} PhoneNumber; 

Wenn ich eine Instanz dieser Struktur erstellen, wenn ich die folgende Syntax verwenden:

PhoneNumber homePhone = {858, 555, 1234}; 

... welche Konstruktor ruft es? Der Standardkonstruktor oder der Kopierkonstruktor oder gar keiner, weil er nicht 'neu' aufruft?

Der eigentliche Zweck dieser Frage ist herauszufinden, wie ich ein viertes Feld hinzufügen kann. Deshalb möchte ich meine Struktur neu definieren als:

typedef 
struct number{ 
    int areaCode; 
    int prefix; 
    int suffix; 
    int extension; // NEW FIELD INTRODUCED 
} PhoneNumber; 

So, jetzt habe ich neue Phone Objekte mit vier Feldern erstellen:

PhoneNumber officePhone = {858, 555, 6789, 777} 

Allerdings habe ich Hunderte dieser Phone Instanzen bereits erstellt mit nur 3 Felder (xxx, xxx, xxxx). Daher möchte ich JEDE einzelne Instanziierung meines bereits definierten PhoneNumber-Objekts nicht ausführen und ändern. Ich möchte in der Lage sein, diese in Ruhe zu lassen, aber immer noch in der Lage sein, neue Telefonnummern-Instanzen mit VIER-Feldern zu erstellen. Also versuche ich herauszufinden, wie ich den Konstruktor überschreiben kann, so dass meine bestehenden Drei-Parameter-Instanziierungen nicht brechen, aber es wird auch meine neuen Vier-Parameter-Instanziierungen unterstützen.

Wenn ich versuche, einen überschreibenden Standardkonstruktor zu definieren, der 3 Felder nimmt und den vierten auf einen Standardwert '0' setzt, bekomme ich Fehler (im Instanziierungsteil des Codes, nicht die Konstruktordefinition), die mein Objekt beschwert muss von Konstruktor initialisiert werden, nicht von {...}. Wenn ich also den Standardkonstruktor überschreibe, kann ich keine geschweiften Klammern mehr verwenden, um meine neuen Objekte zu erstellen?

Entschuldigung, wenn das weg von den ursprünglichen Fragen völlig weggeht.

+0

Verwandte: [http://stackoverflow.com/questions/88957/what-does-0-mean-in-c/88960#88960](http://stackoverflow.com/questions/ 88957/what-does-0-mean-in-c/88960 # 88960) –

Antwort

1

Es ruft effektiv den Standard-ctor auf; Was passiert, ist, dass eine Struktur zugewiesen wird und jedem Wert der Standardwert "=" zugewiesen wird.

+0

Der Standardkonstruktor wird nicht aufgerufen, und auch operator = wird nicht aufgerufen. In C++ 8.5.1 definiert es diese Syntax für die Initialisierung von Aggregaten. –

+0

Hast du * gelesen * was ich geschrieben habe? Was ist der Standardwert "="? –

2

Eine Struktur in C++ ist wie eine Klasse. Der Standardkonstruktor wird aufgerufen. Danach wird jedes Feld mit seinem Zuweisungsoperator kopiert.

+0

Der Standardkonstruktor wird nicht für POD-Typen aufgerufen, und die Felder werden nicht kopiert. Die Syntax definiert die Initialisierung der Mitglieder des Aggregats. Siehe C++ 8.5.1 –

4

Die Mitglieder werden tatsächlich kopieninitialisiert. Der Standardkonstruktor für jeden einzelnen wird nicht aufgerufen, und operator= ist im Gegensatz zu dem, was einige andere Antworten vorschlagen, beteiligt. Es kann durch eine Software namens geordi - alternativ durch Lesen durch den Standard - angezeigt werden. Ich zeige den "Spaß" Weg mit dieser Software. Es hat eine Klasse tracked::B, die uns zeigen kann, wenn Konstruktoren/Kopierkonstruktoren oder Destruktoren/Kopierzuweisungsoperatoren aufgerufen werden. Die Ausgabe zeigt es ist (TRACK Grenzen der Erklärung Tracking es folgende):

B1*(B0) B1~ 

ich diesen Code

verwendet
struct T { tracked::B b; }; int main() { tracked::B b; TRACK T t = { b }; } 

Wie Sie sehen, das zweite B Objekt - die das Mitglied innerhalb der lokalen ist Variable t, wird die Kopie von dem anderen Objekt b initialisiert. Natürlich ist kein Zuweisungsoperator aktiviert. Sie können darüber in 12.6.1/2 im Standard lesen, wenn Sie möchten.

Das gleiche gilt übrigens für Arrays (die ebenfalls Aggregate sind). Viele Leute glauben, dass Objekte, die Mitglied von Arrays sind, einen Typ mit einem Standardkonstruktor haben müssen. Das stimmt aber nicht. Sie können einfach durch ein anderes Objekt ihres Typs initialisiert werden und es wird gut funktionieren.

Alle andere Elemente, die im Aggregat nicht explizit initialisiert wurden, werden initialisiert. Value-Initialisierung ist eine Mischung aus Standard-Initialisierung und Null-Initialisierung. Wenn ein Member einen Typ hat, für den ein Konstruktor vom Benutzer deklariert wurde, wird dieser Konstruktor aufgerufen. Wenn es einen Typ hat, der nicht einen Benutzer deklariert Konstruktor hat, dann ist jedes Mitglied davon Wert initialisiert. Für eingebaute Typen (int, bool, pointer, ...) ist eine Wert-Initialisierung die gleiche wie eine Null-Initialisierung (das bedeutet, dass eine solche Variable Null wird).

struct T { int a, b, c; }; int main() { T t = { 1 }; } 

Diese Initialisierung Regeln sind beängstigend, in der Tat - vor allem, weil die 2003 Version von C++ diesen Wert Initialisierung eingeführt: mit Ausnahme des ersten (a), das sein wird - Im Folgenden wird jedes Element auf Null initialisiert werden. Es ist wasn't part of the Standard ab 1998. Wenn Sie sich für diese geschweiften Initialisierungen interessieren, können Sie How to initialize nested structures in C++? lesen.

3

Es ruft nicht die Standard-Ctor, wie andere geschrieben haben. Vom Konzept her ist es dasselbe, aber in der Praxis finden Sie im Assemblercode keinen Funktionsaufruf.

Stattdessen bleiben die Mitglieder nicht initialisiert; Sie initialisieren sie mit dem geschweiften Klammerkonstrukt.

Interessanterweise folgt aus:

PhoneNumber homePhone = {858, 555, 1234}; 

Ergebnisse in dieser Versammlung (GCC 4.0.1, -O0):

movl $858, -20(%ebp) 
movl $555, -16(%ebp) 
movl $1234, -12(%ebp) 

Nicht viele Überraschungen. Die Assembly ist inline die Funktion, die die obige C++ - Anweisung enthält. Die Werte (beginnend mit $) werden in Offsets in den Stack verschoben (movl) (ebp register). Sie sind negativ, da die Speicherpositionen für die Strukturelemente dem Initialisierungscode vorangehen.

Wenn Sie nicht vollständig die Struktur initialisieren, dh einige Mitglieder wie so auslassen:

PhoneNumber homePhone = {858, 555}; 

... dann bekomme ich folgende Assembler-Code:

movl $0, -20(%ebp) 
movl $0, -16(%ebp) 
movl $0, -12(%ebp) 
movl $858, -20(%ebp) 
movl $555, -16(%ebp) 

scheint, als ob Der Compiler macht dann tatsächlich etwas, das dem Aufruf des Standardkonstruktors sehr ähnlich ist, gefolgt von der Zuweisung. Aber auch dies ist inline in der aufrufenden Funktion, nicht in einem Funktionsaufruf.

Wenn auf der anderen Seite definieren Sie einen Standard-Konstruktor, der die Mitglieder, auf die angegebenen Werte initialisiert, etwa so:

struct PhoneNumber { 
    PhoneNumber() 
    : areaCode(858) 
    , prefix(555) 
    , suffix(1234) 
    { 
    } 

    int areaCode; 
    int prefix; 
    int suffix; 
}; 

PhoneNumber homePhone; 

Dann sind Sie Assembler-Code erhalten, die tatsächlich eine Funktion aufruft, und initialisiert die Datenelemente über einen Zeiger auf die Struktur:

movl 8(%ebp), %eax 
movl $858, (%eax) 
movl 8(%ebp), %eax 
movl $555, 4(%eax) 
movl 8(%ebp), %eax 
movl $1234, 8(%eax) 

jeder Zeile, die movl 8(%ebp), %eax setzt den Zeiger-Wert (EAX-Register) an den Anfang der Daten des struct geht. In den anderen Zeilen wird eax direkt verwendet, mit einem Offset von 4 und einem Offset von 8, ähnlich der Direktstapeladressierung in den vorherigen zwei Beispielen.

Natürlich ist das alles spezifisch für die Compiler-Implementierung, aber ich wäre überrascht, wenn andere Compiler etwas außerordentlich anders machen würden.

+0

Generiert die Verwendung des Standardkonstruktors mit der Elementinitialisierungsliste eine Zeigerdereferenz, wenn die Optimierung aktiviert ist? I.e. mit gcc, entfernt das Kompilieren mit der Option -O3 die Zeigerdereferenzen? – paxos1977

+0

Der Blick auf den Compiler-Ausgang ist kein Ersatz für das Verständnis der Spezifikation. –

+0

@ceretullis: Ich habe nicht gesucht. @ don.neufeld: einverstanden, aber das bedeutet nicht, dass es nutzlos ist, zu verstehen, was tatsächlich passiert. –

0

Allerdings habe ich Hunderte dieser Phone Instanzen erstellt bereits mit nur 3 Felder (xxx, xxx, xxxx). Also ich möchte nicht gehen und ändern jede einzelne Instanz von mein PhoneNumber-Objekt, das bereits definiert ist.

Kein Problem, Sie rufen nur update-instance-for-redefined-Klasse und ..... äh, vergiss es. Fahren Sie fort, um dieses "nicht hilfreich" zu markieren

2

Initialisierung ist, zumindest für mich, einer der härtesten Teile des C++ - Standards. Die Syntax, die Sie verwenden: Aggregate x = { 1, 2, 3, 4 }; definiert die Initialisierung eines Aggregattyps mit den ersten vier Mitgliedern mit den Werten 1, 2, 3 und 4.

Als eine Anmerkung zu Ihrem speziellen Fall (die Struktur ist von 3 bis 4 Elemente gewachsen), die Felder, die nicht in der Initialisierungsliste zwischen den geschweiften Klammern vorhanden sind, werden Wert initialisiert, was insbesondere für skalare Typen entspricht Zero-Initialisierung, die selbst das Zuweisen von 0 ist. So wird Ihr viertes Element initialisiert zu 0.

Referenzen

Es ist alles im C++ - Standard, Kapitel 8.5 und genauer in 8.5.1 Aggregate definiert. Aggregate werden nicht mit einem implizit deklarierten Standardkonstruktor initialisiert. Wenn Sie die obige Syntax verwenden, bitten Sie den Compiler, die angegebenen Felder mit den angegebenen Werten zu initialisieren. Jede zusätzliche Feld in der Summe wird Wert initialisiert

Nun Wert Initialisierung wird als Aufruf definiert für den Benutzer definiert Standardkonstruktor, wenn der Typ eine Klasse mit einer solchen Konstruktor ist. Wenn es sich um ein Array oder eine nicht gewerkschaftliche Klasse ohne benutzerdefinierten Konstruktor handelt, wird jedes der Elementattribute Wert initialisiert. Andernfalls wird das Objekt sein Null initialisiert, die dann wieder als definiert ...

Null Initialisierung wird definiert als der Wert auf 0 für skalare Typen festlegen. Für Klassen wird jedes Klassenmitglied und jede Basisklasse null initialisiert.Für Vereinigungen wird das erste Mitglied (nur) Null initialisiert, für Arrays werden alle Mitglieder Null initialisiert sein.

0

Da an Ihrem Typ kein Konstruktor beteiligt ist, müsste die vorhandene Drei-Parameter-Syntax an der Aggregat-Initialisierungsstelle geändert werden.

Es sei denn, die Semantik Ihres neu hinzugefügten Felds, z. dass neuer Feldtyp 'auf Null gesetzt Design-aware' (Idiom empfohlen von .NET-Programmierung, Brad und Co usw. in Design-Richtlinien Unsinn) ist, können Sie

kann nicht:

a) etwas aussagekräftiger als entweder C liefern ++ Standardparameter, wenn mit etwas Magie ein Verfahren beteiligt war (deault/optional params bald zur Verfügung stehen in den Massenmarkt C# Geschäft in der Nähe des Lebensmittelgeschäft Web 4.0 und für alte COM)

noch können Sie

b) folgen das C# -ähnliche Muster zum Gestalten von Werttypen mit ungültigen Wertmarken als 0s (was in diesem Fall wohl v Sie sind im Allgemeinen schlecht und schlecht, wenn Sie die Kontrolle über die Konstanten nicht haben - etwas, das Bibliotheken auf Quell-Ebene sehr gut tun und alle Java-MS-ähnlichen Frameworks lutschen.

Kurz gesagt, wenn 0 ein gültiger Wert ist, sind Sie vollgestopft und etwas, was die meisten Compiler-Schreiber als nützlich erachtet haben, bevor ein managed Style oder Idiom jemals existiert hat; aber es ist nicht unbedingt richtig.

Wie auch immer, die beste Chance ist nicht C++ oder C#. Es ist Code-Generation, dh. mehr liberale Meta-Programmierung als moderne C++ "Templating" drückt auf .. Sie finden es ähnlich JSON-Array-Annotation und könnte entweder Geist oder (mein Rat wäre, würde) rollen Sie Ihre eigenen Utensilien. Es hilft auf lange Sicht, und schließlich werden Sie besser darin sein wollen (dh, was sie Lame-Man-Modellierung alias Oslo in .NET nennen).

[Probleme wie das sind überall in vielen Sprachen, es ist ein Nachteil aller C-style (C, C++, Java, C#, Sie nennen es); sehr ähnlich zu Arrays von Arrays Hacker für jede Art von domainübergreifenden oder semantischen I/O-Arbeit erforderlich und offensichtlich auch in Web-Tech wie SOAP etc .. neue C++ 0x variadic Bits helfen hier nicht viel aber anscheinend bringen Sie exponentiell schnellere Kompilierzeiten, wenn Sie mit etwas Template-Hacker spielen. Wen interessiert das?]