Frage

Statt zu erinnern, der mit einer einfachen ‚C‘ Struktur zu initialisieren, könnte ich daraus ziehen und es im Konstruktor wie diese Null:

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct()
    {
        memset(this, 0, sizeof(MY_STRUCT));
    }
};

Dieser Trick wird oft verwendet, Win32 Strukturen zu initialisieren und kann so eingestellt manchmal das allgegenwärtige cbSize Mitglied.

Nun, solange es nicht eine virtuelle Funktionstabelle für den Memset Ruf zu zerstören, ist dies eine sichere Praxis?

War es hilfreich?

Lösung

PREAMBLE:

Während meine Antwort ist immer noch in Ordnung ist, finde ich litb Antwort ganz überlegen, weil verminen:

  1. Es lehrt mich ein Trick, den ich nicht kannte (litb die Antworten in der Regel diese Wirkung haben, aber dies ist das erste Mal, dass ich es aufschreiben)
  2. Sie beantwortet genau die Frage (das heißt, die ursprüngliche Struktur des Teils auf Null initialisiert)

Also bitte, litb Antwort vor meinem betrachten. In der Tat, schlage ich vor der Autor Frage litb Antwort als richtig zu betrachten.

Original Antwort

ein echtes Objekt Putting (das heißt std :: string) etc. innen brechen, weil das wahre Objekt wird vor dem Memset initialisiert werden, und dann überschrieben durch Nullen.

die Initialisierungsliste Mit funktioniert nicht für g ++ (Ich bin überrascht, ...). Initialisieren Sie es stattdessen in der CMyStruct Konstruktorrumpf. Es wird C ++ freundlich:

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct() { n1 = 0 ; n2 = 0 ; }
};

P. S .: Ich nahm Sie hatte nicht Kontrolle über my_struct, natürlich. Mit der Kontrolle würden Sie den Konstruktor direkt in my_struct und vergessen über die Vererbung hinzugefügt haben. Beachten Sie, dass Sie nicht-virtuelle Methoden zu einer C-ähnlicher Struktur hinzufügen können, und es immer noch als eine Struktur verhalten haben.

EDIT: Hinzugefügt fehlende Klammer, nachdem Lou Franco-Kommentar. Dank!

EDIT 2: Ich habe versucht, den Code auf g ++, und aus irgendeinem Grund nicht die Initialisierung Liste mit nicht funktionieren. Ich korrigierte den Code, den Körper Konstruktor. Die Lösung ist nach wie vor gültig, wenn.

Bitte meinen Posten neu zu bewerten, da der ursprüngliche Code (siehe Changelog für weitere Informationen) geändert wurde.

EDIT 3: Nach dem Robs Kommentar zu lesen, ich denke, er einen Punkt verdient Diskussion hat: „Einverstanden, aber dies könnte eine enorme Win32 Struktur sein, die mit einem neuen SDK kann sich ändern, so dass ein memset ist zukunftssicher“

Ich bin nicht einverstanden: Zu wissen, Microsoft, wird es nicht wegen ihrer Notwendigkeit für eine perfekte Abwärtskompatibilität ändern. Sie werden stattdessen ein erweiterte my_struct Ex Struktur mit dem gleichen Anfang Layout wie my_struct, mit zusaetzliche Mitgliedern am Ende, und erkennbar durch einen „Größe“ Membervariable wie die für ein Registerfenster, IIRC verwendet Struktur erstellen.

So ist die einzig gültige von Robs Kommentar verbleibenden ist der „enorme“ struct. In diesem Fall ist vielleicht ein memset bequeme, aber Sie werden my_struct ein variable Mitglied CMyStruct zu machen, anstatt von ihm erben.

Ich sehe ein anderes Hack, aber ich denke, dies ist wegen der möglichen Struktur Ausrichtungsproblem brechen würde.

EDIT 4: Bitte werfen Sie einen Blick auf Frank Krueger-Lösung. Ich kann es nicht versprechen, tragbare (Ich denke, es ist), aber es ist immer noch interessant aus technischer Sicht, weil es einen Fall zeigt, wo, in C ++, die „dieses“ Zeiger „Adresse“ bewegt sich von seiner Basisklasse zu seiner vererbten Klasse .

Andere Tipps

Sie können einfach Wert initialisieren Basis, und alle ihre Mitglieder zero'ed geführt werden. Dies ist gewährleistet

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct():MY_STRUCT() { }
};

Dazu arbeiten sollte es keine Benutzer erklärten Konstruktor in der Basisklasse sein, wie in Ihrem Beispiel.

Kein böser memset dafür. Es ist nicht garantiert, dass memset in Ihrem Code funktioniert, obwohl es in der Praxis funktionieren soll.

Viel besser als ein memset, können Sie diesen kleinen Trick statt:

MY_STRUCT foo = { 0 };

Damit wird alle Mitglieder auf 0 (oder dessen Standardwert iirc) initialisiert wird, keine Notwendigkeit, einen Wert für jede specifiy.

Das würde mich viel sicherer fühlen, wie es auch funktionieren soll, wenn es eine vtable ist (oder die Compiler schreien).

memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

Ich bin sicher, dass Ihre Lösung funktionieren wird, aber ich bezweifle es irgendwelche Garantien gemacht werden, wenn memset und Klassen zu mischen.

Dies ist ein perfektes Beispiel ein C Idiom C portiert ++ (und warum es nicht immer funktionieren ...)

Das Problem, das Sie mit der Verwendung von memset haben, ist, dass in C ++, eine Struktur und eine Klasse ist genau die gleiche, außer dass standardmäßig eine Struktur öffentliche Sichtbarkeit und eine Klasse hat eine eigene Sicht.

So was später, wenn auf, ändert sich einige gut gemeinte Programmierer my_struct etwa so:


struct MY_STRUCT
{
    int n1;
    int n2;

   // Provide a default implementation...
   virtual int add() {return n1 + n2;}  
};

Durch das Hinzufügen, dass einzelne Funktion, könnte Ihr Memset jetzt Chaos verursachen. Es gibt eine ausführliche Diskussion in comp.lang.c +

Die Beispiele haben "nicht spezifiziert Verhalten".

Für einen nicht-POD, die Reihenfolge, in der der Compiler ein Objekt legt (alle Basen Klassen und Mitglieder) ist nicht spezifiziert (ISO C ++ 03.10). Betrachten Sie das folgende:

struct A {
  int i;
};

class B : public A {       // 'B' is not a POD
public:
  B ();

private:
  int j;
};

Dies angelegt wie werden kann:

[ int i ][ int j ]

Oder wie:

[ int j ][ int i ]

Daher ist die Verwendung Memset direkt an der Adresse von ‚this‘ ist sehr viel nicht spezifiziert Verhalten. Eine der Antworten oben, auf den ersten Blick sieht sicherer zu sein:

 memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

Ich glaube jedoch, dass dies auch Ergebnisse in nicht näher bezeichnete Verhalten streng genommen. Ich kann nicht den normativen Text finden, aber die Notiz in 05.10 sagt: „Eine Basisklasse subobject ein Layout haben (3.7) unterscheidet sich von dem Layout eines am meisten abgeleitete Objekt des gleichen Typs“.

Als Ergebnis I-Compiler Raum Optimierungen mit den verschiedenen Mitgliedern durchführen konnte:

struct A {
  char c1;
};

struct B {
  char c2;
  char c3;
  char c4;
  int i;
};

class C : public A, public B
{
public:
  C () 
  :  c1 (10);
  {
    memset(static_cast<B*>(this), 0, sizeof(B));      
  }
};

angelegt werden kann als:

[ char c1 ] [ char c2, char c3, char c4, int i ]

Auf einem 32-Bit-System, aufgrund alighments usw. für 'B', sizeof (B) wird höchstwahrscheinlich 8 Bytes sein. Jedoch sizeof (C) kann auch ‚8‘ Bytes, wenn der Compiler die Datenelemente packt. Daher der Aufruf von memset könnte den Wert überschreibt ‚c1‘ gegeben.

Precise Layout einer Klasse oder Struktur nicht in C ++ garantiert ist, weshalb Sie keine Annahmen über die Größe von außen machen sollten (das heißt, wenn Sie nicht ein Compiler sind).

Wahrscheinlich funktioniert es, bis Sie einen Compiler finden, auf dem es nicht, oder Sie etwas VTable in die Waagschale werfen.

Wenn Sie bereits einen Konstruktor haben, warum es nicht nur dort initialisieren mit n1 = 0; n2 = 0; -. Das ist sicher der normale Weg

Edit:. Eigentlich wie paercebal gezeigt hat, Ctor Initialisierung ist noch besser

Meine Meinung ist, nicht. Ich bin mir nicht sicher, was es gewinnt auch nicht.

Wie Ihre Definition von CMyStruct Änderungen und Sie hinzufügen / löschen Mitglieder, kann dies zu Fehlern führt. Leicht.

Erstellen Sie einen Konstruktor für CMyStruct, die ein MyStruct einen Parameter hat.

CMyStruct::CMyStruct(MyStruct &)

oder etwas in dem gesucht. Sie können dann ein öffentliches oder privates ‚MyStruct‘ Mitglied initialisieren.

von einem ISO-C ++ Sicht gibt es zwei Probleme:

(1) Ist das Objekt ein POD? Die Abkürzung steht für Plain Old Daten, und die Standard-aufzählt, was Sie nicht in einem POD haben können (Wikipedia hat eine gute Zusammenfassung). Wenn es nicht ein POD ist, können Sie es nicht memset.

(2) Gibt es Mitglieder, für die all-Bit-Null ungültig ist? Unter Windows und Unix ist der NULL-Zeiger alle Bits Null; es muss nicht sein. Floating-Point-0 hat alle Bits in IEEE754 Null, was durchaus üblich ist, und auf x86.

Frank Krügers Spitze richtet sich Ihre Anliegen durch die memset auf die POD Basis des nicht-POD-Klasse zu beschränken.

Versuchen Sie das. - Überlastung neue

EDIT: Ich sollte hinzufügen - Dieses ist sicher, weil der Speicher vor auf Null gestellt wird jeder Bauer genannt werden. Große Fehler -. Funktionieren nur, wenn das Objekt dynamisch zugewiesen

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct()
    {
        // whatever
    }
    void* new(size_t size)
    {
        // dangerous
        return memset(malloc(size),0,size);
        // better
        if (void *p = malloc(size))
        {
            return (memset(p, 0, size));
        }
        else
        {
            throw bad_alloc();
        }
    }
    void delete(void *p, size_t size)
    {
        free(p);
    }

};

Wenn my_struct Ihr Code ist, und Sie sind glücklich, einen C ++ Compiler verwenden, können Sie den Konstruktor setzen es in einer Klasse ohne Verpackung:

struct MY_STRUCT
{
  int n1;
  int n2;
  MY_STRUCT(): n1(0), n2(0) {}
};

Ich bin mir nicht sicher über die Effizienz, aber ich hasse Tricks tun, wenn Sie nicht die Effizienz bewiesen haben, ist erforderlich.

Kommentar zu litb Antwort (scheint, dass ich ist noch nicht erlaubt Kommentar direkt):

Auch bei dieser schönen C ++ -. Stil Lösung, die Sie sehr vorsichtig sein, dass Sie diese naiv auf eine Struktur nicht durchführen ein nicht-POD Element enthält,

Einige Compiler dann initialisieren nicht mehr korrekt.

Siehe diese Antwort auf eine ähnliche Frage . Ich persönlich hatte die schlechte Erfahrung auf VC2008 mit einer zusätzlichen std :: string.

Was ich tue, ist Aggregat Initialisierung verwenden, sondern nur die Angabe initializers für Mitglieder mich interessiert, z:

STARTUPINFO si = {
    sizeof si,      /*cb*/
    0,              /*lpReserved*/
    0,              /*lpDesktop*/
    "my window"     /*lpTitle*/
};

Die übrigen Mitglieder werden zu Nullen des entsprechenden Typs initialisiert werden (wie in Drealmer der Post). Hier legen Sie vertrauen Microsoft nicht grundlos Kompatibilität zu brechen, indem sie neue Strukturelemente in der Mitte Zugabe (eine vernünftige Annahme). Diese Lösung erscheint mir als optimal -. Eine Aussage, keine Klassen, keine memset, keine Annahmen über die interne Darstellung von Gleitkomma-Null oder Null-Zeiger

Ich denke, die Hacks Vererbung beteiligt schrecklicher Stil. Öffentliche Vererbung bedeutet IS-A für die meisten Leser. Beachten Sie auch, dass Sie von einer Klasse sind zu erben, die nicht dafür ausgelegt ist, eine Basis zu sein. Da es keinen virtuellen Destruktor ist, Kunden, die eine abgeleitete Klasse Instanz über einen Zeiger auf Basis löscht nicht definiertes Verhalten aufrufen.

Ich nehme an, die Struktur zur Verfügung gestellt wird und nicht geändert werden kann. Wenn Sie die Struktur ändern kann, dann ist die offensichtliche Lösung ist das Hinzufügen einer Konstruktor.

Sie Ingenieur nicht über Ihren Code mit C ++ Wrapper, wenn alles, was Sie wollen ein einfaches Makro ist Ihre Struktur zu initialisieren.

#include <stdio.h>

#define MY_STRUCT(x) MY_STRUCT x = {0}

struct MY_STRUCT
{
    int n1;
    int n2;
};

int main(int argc, char *argv[])
{
    MY_STRUCT(s);

    printf("n1(%d),n2(%d)\n", s.n1, s.n2);

    return 0;
}

Es ist ein Stück Code, aber es ist wiederverwendbar; einmal ist es, und es sollte für jeden POD arbeiten. Sie können eine Instanz dieser Klasse zu jeder Funktion übergeben einen my_struct erwartet, oder verwenden Sie die GetPointer Funktion in eine Funktion zu übergeben, die die Struktur ändern wird.

template <typename STR>
class CStructWrapper
{
private:
    STR MyStruct;

public:
    CStructWrapper() { STR temp = {}; MyStruct = temp;}
    CStructWrapper(const STR &myStruct) : MyStruct(myStruct) {}

    operator STR &() { return MyStruct; }
    operator const STR &() const { return MyStruct; }

    STR *GetPointer() { return &MyStruct; }
};

CStructWrapper<MY_STRUCT> myStruct;
CStructWrapper<ANOTHER_STRUCT> anotherStruct;

Auf diese Weise müssen Sie sich keine Gedanken darüber machen, ob NULL-Werte sind alle 0 oder Punktdarstellungen schweben. Solange STR ein einfacher Aggregat-Typ ist, werden die Dinge funktionieren. Wenn STR keine einfacher Aggregattyp ist, erhalten Sie einen Fehler bei der Kompilierung erhalten, so dass Sie diese nicht über versehentlich missbräuchlich kümmern. Auch wenn der Typ enthält etwas komplexer, solange es einen Standardkonstruktor hat, du bist ok:

struct MY_STRUCT2
{
    int n1;
    std::string s1;
};

CStructWrapper<MY_STRUCT2> myStruct2; // n1 is set to 0, s1 is set to "";

Auf der anderen Seite, es ist langsamer, da Sie eine zusätzliche temporäre Kopie machen, und der Compiler wird jedes Mitglied 0 einzeln statt einer Memset zuweisen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top