377 Stimmen

Initialisierung von C++-Strukturen

Ist es möglich, Structs in C++ wie unten angegeben zu initialisieren:

struct address {
    int street_no;
    char *street_name;
    char *city;
    char *prov;
    char *postal_code;
};

address temp_address = { .city = "Hamilton", .prov = "Ontario" };

Die Links aquí y aquí erwähnen, dass es nur in C möglich ist, diesen Stil zu verwenden. Wenn ja, warum ist dies in C++ nicht möglich? Gibt es einen technischen Grund, warum es nicht in C++ implementiert ist, oder ist es schlechte Praxis, diesen Stil zu verwenden. Ich verwende diese Art der Initialisierung gerne, weil meine Struktur groß ist und dieser Stil mir eine klare Lesbarkeit gibt, welcher Wert welchem Element zugewiesen ist.

Bitte teilen Sie mir mit, ob es andere Möglichkeiten gibt, die gleiche Lesbarkeit zu erreichen.

Ich habe die folgenden Links gelesen, bevor ich diese Frage gestellt habe:

  1. C/C++ für AIX
  2. C Strukturinitialisierung mit Variable
  3. Statische Strukturinitialisierung mit Tags in C++
  4. C++11 Richtige Strukturinitialisierung

4voto

Jan Turoň Punkte 28722

In C++ wurden die Initialisierer im C-Stil durch Konstruktoren ersetzt, die zur Kompilierzeit sicherstellen können, dass nur gültige Initialisierungen durchgeführt werden (d.h. nach der Initialisierung sind die Objektmitglieder konsistent).

Das ist eine gute Praxis, aber manchmal ist eine Vorinitialisierung praktisch, wie in Ihrem Beispiel. OOP löst dieses Problem durch abstrakte Klassen oder gestalterische Entwurfsmuster .

Meiner Meinung nach wird durch diesen sicheren Weg die Einfachheit zunichte gemacht, und manchmal könnte der Kompromiss bei der Sicherheit zu teuer sein, da einfacher Code kein ausgeklügeltes Design benötigt, um wartbar zu bleiben.

Als alternative Lösung schlage ich vor, Makros mit Lambdas zu definieren, um die Initialisierung so zu vereinfachen, dass sie fast wie in C aussieht:

struct address {
  int street_no;
  const char *street_name;
  const char *city;
  const char *prov;
  const char *postal_code;
};
#define ADDRESS_OPEN [] { address _={};
#define ADDRESS_CLOSE ; return _; }()
#define ADDRESS(x) ADDRESS_OPEN x ADDRESS_CLOSE

Das Makro ADDRESS wird erweitert zu

[] { address _={}; /* definition... */ ; return _; }()

der das Lambda erzeugt und aufruft. Makro-Parameter sind ebenfalls durch Kommata getrennt, daher müssen Sie den Initialisierer in Klammern setzen und wie folgt aufrufen

address temp_address = ADDRESS(( _.city = "Hamilton", _.prov = "Ontario" ));

Sie könnten auch einen verallgemeinerten Makroinitialisierer schreiben

#define INIT_OPEN(type) [] { type _={};
#define INIT_CLOSE ; return _; }()
#define INIT(type,x) INIT_OPEN(type) x INIT_CLOSE

aber dann ist der Anruf etwas weniger schön

address temp_address = INIT(address,( _.city = "Hamilton", _.prov = "Ontario" ));

Sie können das ADDRESS-Makro jedoch problemlos mit dem allgemeinen INIT-Makro definieren

#define ADDRESS(x) INIT(address,x)

4voto

VincentP Punkte 66

Ich habe diese Methode für globale Variablen gefunden, bei der die ursprüngliche Strukturdefinition nicht geändert werden muss:

struct address {
             int street_no;
             char *street_name;
             char *city;
             char *prov;
             char *postal_code;
           };

deklarieren Sie dann die Variable eines neuen Typs, der vom ursprünglichen Typ struct geerbt wurde, und verwenden Sie den Konstruktor für die Initialisierung der Felder:

struct temp_address : address { temp_address() { 
    city = "Hamilton"; 
    prov = "Ontario"; 
} } temp_address;

Allerdings nicht ganz so elegant wie der C-Stil ...

Für eine lokale Variable ist ein zusätzliches memset(this, 0, sizeof(*this)) am Anfang des Konstruktors erforderlich, also ist es eindeutig nicht schlechter und die Antwort von @gui13 ist angemessener.

(Beachten Sie, dass "temp_address" eine Variable des Typs "temp_address" ist, aber dieser neue Typ erbt von "address" und kann überall dort verwendet werden, wo "address" erwartet wird, es ist also OK).

4voto

Seph Reed Punkte 6186

Inspiriert durch diese wirklich tolle Antwort: ( https://stackoverflow.com/a/49572324/4808079 )

Sie können Lambenverschlüsse machen:

// Nobody wants to remember the order of these things
struct SomeBigStruct {
  int min = 1;
  int mean = 3 ;
  int mode = 5;
  int max = 10;
  string name;
  string nickname;
  ... // the list goes on
}

.

class SomeClass {
  static const inline SomeBigStruct voiceAmps = []{
    ModulationTarget $ {};
    $.min = 0;  
    $.nickname = "Bobby";
    $.bloodtype = "O-";
    return $;
  }();
}

Oder, wenn Sie sehr ausgefallen sein wollen

#define DesignatedInit(T, ...)\
  []{ T ${}; __VA_ARGS__; return $; }()

class SomeClass {
  static const inline SomeBigStruct voiceAmps = DesignatedInit(
    ModulationTarget,
    $.min = 0,
    $.nickname = "Bobby",
    $.bloodtype = "O-",
  );
}

Dies hat einige Nachteile, die hauptsächlich mit nicht initialisierten Mitgliedern zu tun haben. Von dem, was die verlinkten Antworten Kommentare sagen, kompiliert es effizient, obwohl ich es nicht getestet habe.

Insgesamt halte ich das für einen guten Ansatz.

3voto

rbaleksandar Punkte 7692

Sie haben

  1. Die Standard-Initialisierungsliste

    address temp_address {
        /* street_no */,
        /* street_name */,
        ...
        /* postal_code */
    };
    
    address temp_address2 = {
        /* street_no */,
        /* street_name */,
        ...
        /* postal_code */
    }
  2. Die Punktnotation

    address temp_address;
    temp_address.street_no = ...;
    temp_address.street_name = ...;
    ...
    temp_address.postal_code = ...;
  3. Die vorgesehene Aggregatinitialisierung, wobei die Initialisierungsliste die Bezeichnungen der einzelnen Elemente der Struktur enthält (siehe Dokumentation ) ab C++20 verfügbar.

  4. Die Behandlung eines struct wie eine C++-Klasse - in C++ sind Strukturen eigentlich spezielle Arten von Klassen, bei denen alle Mitglieder public (im Gegensatz zu einer Standard-C++-Klasse, bei der alle Mitglieder private wenn nicht explizit anders angegeben) und dass sie bei der Vererbung standardmäßig auf public :

    struct Address {
        int street_no;
        ...
        char* postal_code;
    
        Address (int _street_no, ... , char* _postal_code)
         : street_no(_street_no),
           ...
           postal_code(_postal_code)
        {}
    }
    
    ...
    
     Address temp_address ( /* street_no */, ..., /* postal_code */);

Bei der Art und Weise, wie Sie Ihre Struktur initialisieren, sollten Sie die folgenden Aspekte berücksichtigen:

  • Tragbarkeit - Unterschiedliche Compiler, ein unterschiedlicher Grad an Vollständigkeit des C++-Standards und unterschiedliche C++-Standards insgesamt schränken Ihre Möglichkeiten ein. Wenn Sie mit einem C++11-Compiler arbeiten müssen, aber die in C++20 vorgesehene Aggregatinitialisierung verwenden wollen, haben Sie Pech
  • Lesbarkeit - was besser lesbar ist: temp_address.city = "Toronto" o temp_address { ..., "Toronto", ... } ? Die Lesbarkeit Ihres Codes ist sehr wichtig. Vor allem bei großen Strukturen (schlimmer noch - verschachtelten Strukturen) ist es sehr problematisch, wenn überall unbeschriftete Werte stehen.
  • Skalierbarkeit - Alles, was von einer bestimmten Reihenfolge abhängt, ist keine gute Idee. Das Gleiche gilt für fehlende Bezeichnungen. Sie wollen ein Element im Adressraum der Struktur nach oben oder unten verschieben? Viel Glück mit einer unbeschrifteten Initialisierungsliste (die Suche nach vertauschten Werten bei der Strukturinitialisierung ist ein Alptraum)... Sie wollen ein neues Mitglied hinzufügen? Wiederum viel Glück mit allem, was von einer bestimmten Reihenfolge abhängt.

Während die Punktnotation bedeutet, dass Sie mehr tippen müssen, überwiegen die Vorteile, die Sie durch die Verwendung dieser Notation erhalten, und als solche kann ich sie empfehlen, es sei denn, Sie haben eine kleine Struktur, die zukunftssicher ist, was das Fehlen von Änderungen in ihrer Struktur angeht. In diesem Fall können Sie es sich leisten, mit einer Initialisierungsliste zu arbeiten. Denken Sie daran: Wenn Sie mit anderen Leuten zusammenarbeiten, ist es wichtig, dass Sie einen Code schreiben, der leicht nachvollziehbar ist.

3voto

The Science Boy Punkte 341

In GNUC++ (scheint seit 2.5 veraltet zu sein, vor langer Zeit :) Siehe die Antworten hier: C struct-Initialisierung mit Labels. Es funktioniert, aber wie? ), ist es möglich, eine Struktur wie folgt zu initialisieren:

struct inventory_item {
    int bananas;
    int apples;
    int pineapples;
};

inventory_item first_item = {
    bananas: 2,
    apples: 49,
    pineapples: 4
};

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X