2009-04-22 13 views
3

Eine breite Palette von Strukturen wird in Win32-Programmierung verwendet. Viele Male werden nur einige ihrer Felder verwendet und alle anderen Felder werden auf Null gesetzt. Zum Beispiel:Zeroing einer Struktur im Konstruktor

STARTUPINFO startupInfo; // has more than 10 member variables 
ZeroMemory(&startupInfo, sizeof(startupInfo)); //zero out 
startupInfo.cb = sizeof(startupInfo); //setting size is required according to MSDN 
startupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK; 
//Now call CreateProcess() passing the startupInfo into it 

Ich möchte Kopie-Einfügen von solchen Code zu stoppen und stattdessen eine Abstraktion verwenden, die über Nullsetzen und Einstellen der Parameter sorgen würden. Nehmen wir an, ich brauche nur die Struktur initialisiert wie in Beispiel, und keine andere Abstimmung wird jemals benötigt. Ist das Folgende eine gute Lösung? Was sind mögliche Probleme?

class CStartupInfo : public STARTUPINFO { 
public: 
    CStartupInfo() 
    { 
     ZeroMemory(this, sizeof(STARTUPINFO)); 
     cb = sizeof(STARTUPINFO); 
     dwFlags = STARTF_FORCEOFFFEEDBACK; 
    } 
}; 

Ich bin insbesondere besorgt über die Zeromemory() -Aufruf - sieht aus wie ich voll und ganz den Code steuern und die Klasse hat keine Vtable und ruft Zeromemory() auf diese Weise sicher ist, und es gibt keinen großen Unterschied zwischen den beiden Code-Snippets, außer dass letzterer eine Abstraktion bereitstellt. Gibt es irgendwelche Vorbehalte?

Antwort

1

Ich denke, das ist eine gute Möglichkeit, solche Strukturen kugelsicherer zu machen. Ich bin mir nicht sicher, warum andere die Technik nicht mögen. Ich benutze es gelegentlich, aber nicht so oft, wie ich es sonst tun könnte, weil es aus irgendeinem Grund nicht sehr beliebt bei Kollegen ist.

Ich sehe es nicht sehr oft in der veröffentlichten Material verwendet - die einzige, die ich jetzt in einer schnellen Google finden konnte, ist ein Artikel von Paul DiLascia in MSJ August 1997 (http://www.microsoft.com/MSJ/0897/C0897.aspx):

CRebarInfo und CRebarBandInfo sind programmiererfreundliche C++ - Versionen der C-Structs REBARINFO und REBARBANDINFO mit Konstruktoren, die die Objekte auf alle Nullen initialisieren, bevor das Element cbSize entsprechend festgelegt wird.

Ich kann nicht viel in der Art der Nachteile (außer der fehlenden Akzeptanz) denken. Wenn jemand anders auf etwas Konkreteres hinweisen könnte, würde ich es begrüßen.

+0

Ich mag mich irren, aber diese Klassen scheinen Teil von MFC zu sein? Wenn das so ist, dann ist das ein großer Rahmen, der für diese einfache Bequemlichkeit verwendet werden kann. (Das und ich bin ein ATL/WTL Mann selbst.) – Daemin

+0

Die Strukturen DiLascia Wraps sind Teil des Windows SDK, wenn ich mich nicht irre, aber die Technik kann auf jeder POD-Struktur verwendet werden - es ist nicht an MFC, ATL gebunden oder Windows. Im DiLascia-Artikel geht es um die Arbeit mit einem MFC-Widget - es geht nicht darum, die Strukturen in einer Klasse zu verpacken, die sie enthält. Das Wrappen der Strukturen ist nur eine Technik, die er benutzt und in dem Artikel erwähnt. –

4

Anstatt Unterklassen, warum nicht stattdessen eine Funktion erstellen?

STARTUPINFO CreateStartupInfo(DWORD flags) { 
    STARTUPINFO info; 
    ZeroMemory(&info, sizeof(info)); 
    info.cb = sizeof(STARTUPINFO); 
    info.dwFlags = flags; 
    return info; 
} 

Wahre dies eine nicht-triviale Größe Struktur auf den Stapel gelegt werden. Wenn der betreffende Compiler eine benannte Rückgabewertoptimierung hat (link), wird nur eine Kopie erstellt. Aber auf jeden Fall, Ihr Beispiel, das Sie schon einmal gehabt haben und vorübergehend eine Sekunde setzen, wird wahrscheinlich keine großen Probleme verursachen.

Ich Unterklasse normalerweise nur eine Struktur, wenn es eine Ressource gibt, die nicht ordnungsgemäß in der Struktur verwaltet wird. Normalerweise wird ein RAII-Modell implementiert. In Ihrem speziellen Beispiel tritt keine zusätzliche Ressourcenverwaltung auf, daher würde ich die Unterklasse vermeiden und einfach eine Funktion verwenden.

+1

Gute Lösung. Warum? Weil der Compiler es komplett durch NRVO optimieren wird! * Es wird keine zweite Kopie * der Struktur auf dem Stapel erstellt, es findet kein Kopieren des Rückgabewertes statt (wenn das obige in einer Initialisierung verwendet wird). Die Funktion hat einfach und rein null Overhead. –

+1

@Konrad, ah ja, ich habe NRVO vergessen: http://msdn.microsoft.com/en-us/library/ms364057.aspx – JaredPar

-1

Sie spezielle Wrapper von Typen erstellen können, die wie gewöhnliche Typen verhalten, aber mit Nullen initialisiert, wie:

class zbool { 
private: 
    bool value; 
public: 
    zbool(const bool value) { ... } 
    operator bool() { ... } 
    // ... code skipped 
}; 

Und dann solche Typen verwenden:

struct MyStruct { 
    zbool deleted; 
}; 

Natürlich wird dies nicht funktionieren, wenn Sie versuchen, externe Strukturen zu initialisieren.

+2

-1 Sie können nicht von eingebauten Typen in C++ ableiten. –

+0

Ja, ich kann nicht. Sorry, ich habe seit Jahren nicht mehr in C++ geschrieben, aber du hast die Idee. – stepancheg

5

Für Strukturen können Sie tun:

STARTUPINFO startup_info = { sizeof(STARTUPINFO), 0 }; 
startup_info.dwFlags = STARTF_FORCEOFFFEEDBACK; 

, die finde ich ein netter Trick ist diese Art von Strukturen zu initialisieren. Der Catch ist jedoch, dass das Feld cb (oder Größe/Länge) das erste in der Struktur sein muss. Man könnte auch einfach einmal die erweiterte Version wenn nötig:

STARTUPINFO startup_info = { 0 }; 
startup_info.cb = sizeof(STARTUPINFO); 
startup_info.dwFlags = STARTF_FORCEOFFFEEDBACK; 

Wenn Sie die Strukturen mit einer Klasse wickeln möchte ich würde empfehlen, versuchen Sie ATL/WTL erste, da die Strukturen, die Sie vielleicht schon sind Einwickeln existieren als Klassen dort.

Wenn Sie immer noch daran interessiert waren, Ihre eigene Klasse zu erstellen, würde ich vorschlagen, dass Sie einen Konstruktor erstellen, der jedes Element der Struktur mit vorgegebenen Standardparametern übernimmt, damit diese später einfacher geändert werden können.

+0

In C++ brauchen Sie nicht einmal die 0. – dalle

+0

Kleiner Punkt: Sie brauchen die Nullen nicht. STARTUPINFO startup_info = {sizeof (STARTUPINFO)}; ist ausreichend (der Rest der Struktur ist Wert initialisiert) –

+1

Sind Sie sicher? Gemäß der Philosophie, die Kosten nicht zu bezahlen, wenn Sie nicht wollen, würde ich annehmen, dass nicht initialisierte Strukturen standardmäßig Müll in sich haben würden. Unabhängig davon würde ich die Null setzen, um explizit zu sein. – Daemin

2

können Sie eine Vorlage verwenden:

template <class T> 
class selfzero : public T 
{ 
public: 
    selfzero() { 
     ZeroMemory(this, sizeof(selfzero<T>)); 
    }; 
}; 

und dann:

{ 
    selfzero<STARTUPINFO> si; 
} 

Der Nachteil: Verwenden Sie diese auf einer Klasse oder Struktur, die eine V-Tabelle oder bekam später eine VTable hat, und es wird puffen.

3

ich tonj Vorschlag benutzt habe, aber da es intellisense tötet oft, ich landete Bevorzugung des this:

template <typename T> 
T& ZeroInit(T & data) 
{ 
    ZeroMemory(&data, sizeof(data)); 
    return data; 
} 

template <typename T> 
T& ZeroInitCB(T & data) 
{ 
    ZeroMemory(&data, sizeof(data)); 
    data.cb = sizeof(data); 
    return data; 
} 

Im Vergleich < selfzero> Es ist eine andere Linie im Normalfall:

STARTUPINFO si; 
ZeroInitCB(si); 

aber - wie gesagt - ich entschied sich für IntelliSense helfen;)

Die Rückkehr T & manchmal Verkettungs erlaubt, aber ich bin nicht mit es so oft.

1

Um auf Tonj Lösung zu verbessern:

template <class T> 
class selfzero : public T 
{ 
public: 
    selfzero() { 
     ZeroMemory((T*) this, sizeof(T)); 
    }; 
}; 

Zeroes T, nicht selfdata. Sogar sicher in Fällen von Mehrfachvererbung, VTables und Ähnlichem. Die T-Struktur muss zusammenhängend im Speicher zugewiesen sein, und ((T *) dies ist) richtig für andere Basisklassen und VTabellen angepasst.

0

, wenn alle nicht-statische Datenelemente der Struktur haben trival Konstrukteurs Ihre Klasse hat eine triviale Konstruktor, auf die Standardwerte zu intialize Mitglieder geht. Die meiste Zeit ist genau dasselbe wie die Struktur absichtlich auf Null zu setzen. Während es vielleicht "gut" ist, sie für die Initialisierung auf Null zu setzen, wird es die meiste Zeit nicht nötig sein.

+0

Integrierte Typen werden standardmäßig nicht initialisiert, es sei denn, ein Initialisierer wird explizit in die Liste benutzerdefinierter Konstruktorinitialisierer aufgenommen. An den Strukturen aus WinAPI-Headern sind keine benutzerdefinierten Konstruktoren vorhanden. – sharptooth

+0

Versuchen Sie es selbst: main.cc

  #include  #include  #include  #include  #include  #include  using namespace std; class P { public: P() { cout << "P()" << endl; } }; struct T { P p; int a; int b; }; int main(int argc, char *argv[]) { T t; }  
Es gibt "P()" aus – piotr

Verwandte Themen