2009-04-22 9 views
3

Win32プログラミングでは、幅広い構造体が使用されます。多くの場合、フィールドの一部だけが使用され、その他のフィールドはすべてゼロに設定されます。例:コンストラクタの構造体をゼロ化する

このようなコードをコピーして貼り付けるのをやめ、その代わりにパラメータのゼロ設定と設定を心配する抽象化を使用したいと考えています。例のように初期化された構造体だけが必要であると仮定しましょう。他のチューニングは必要ありません。以下は良い解決策ですか?起こりうる問題は何ですか?

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

私は特に呼び出しが)(ZeroMemoryが心配です - 私は完全にコードを制御するように見えるし、クラスにはvtableのとを持っていない呼び出しZeroMemory()この方法は安全であり、両者の間に大きな違いはありません後者が抽象化を提供することを除いて、コードスニペット。注意点はありますか?

答えて

1

これは、このような構造をより頑丈にするうえで良い方法だと思います。なぜ他の人がそのテクニックが気に入らないように見えるのか分かりません。私はそれを時折使用しますが、何らかの理由で同僚が非常に好きではないように思われるので、そうでない場合は頻繁に使用しません。

私はそれが非常に頻繁に出版された資料で使用される表示されていない - 私は今、迅速グーグルで見つけることができる唯一の人は、MSJ 1997年8月(http://www.microsoft.com/MSJ/0897/C0897.aspx)でポール・ディラシアの記事です:

CRebarInfoおよびCRebarBandInfoは、C構造体REBARINFOおよびREBARBANDINFOのプログラマフレンドリなC++バージョンであり、cbSizeメンバを適切に設定する前にオブジェクトをすべてゼロに初期化するコンストラクタを使用します。

私は、欠点(受け入れの欠如を除いて)の方法ではあまり考えられません。他の誰かがもっと具体的なものを指すことができるなら、私はそれを感謝します。

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

ゼロT、ないselfdata:TonJのソリューションを改善する

+0

私は間違っているかもしれませんが、それらのクラスはMFCの一部であるようですか?もしそうなら、これは、この単純な利便性のために使用する大きなフレームワークの一種です。 (これと私はATL/WTLの人です。) – Daemin

+0

構造体DiLasciaは、誤解がなければWindows SDKの一部ですが、このテクニックはどのPOD構造体にも使用できます.MFC、ATL 、またはWindows。 DiLasciaの記事はいくつかのMFCウィジェットで動作することについてです。それは、それらを埋め込んでいるクラスに構造体をラップすることではありません。構造体をラップすることは、彼が使用しているテクニンクだけであり、記事を渡す際に言及しています。 –

4

サブクラス化する代わりに、代わりに関数を作成してみましょう。

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

Trueこれは、スタック上に単純なサイズの構造体を置くことができます。問題のコンパイラが名前付き戻り値最適化(link)を行った場合、作成されるコピーは1つだけです。しかし、いずれにしても、インスタンスを一度作成して一時的に2番目に配置した例は、大きな問題を引き起こす可能性は低いです。

通常、構造体に適切に管理されていないリソースがある場合は、構造体をサブクラス化するだけです。通常は、RAIIモデルを実装することです。あなたの特定の例では、余分なリソース管理が行われていないので、私はサブクラスを避け、関数を使うだけです。

+1

良い解決策。どうして?コンパイラはNRVOを介して完全に離れて最適化するためです! *構造体の2番目のコピー*はスタックには作成されません。戻り値のコピーは行われません(初期化で上記が使用されている場合)。この機能は単純で純粋にオーバーヘッドがゼロです。 –

+1

@Konrad、はいはい私はNRVOについて忘れました:http://msdn.microsoft.com/en-us/library/ms364057.aspx – JaredPar

-1

あなたは普通のタイプのように振る舞うタイプの特別なラッパーを作成しますが、同様に、ゼロで初期化することができます

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

そして、このようなタイプの使用:

struct MyStruct { 
    zbool deleted; 
}; 

を、もちろんこれはしません外部構造を初期化しようとするとうまくいきます。

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

私は構造のこれらの並べ替えを初期化するための巧妙なトリックです見つける:あなたがすることができる構造のために

+2

-1 C++の組み込み型から派生することはできません。 –

+0

はい、できません。申し訳ありませんが、私はC++で何年も書いていませんが、あなたはその考えを持っています。 – stepancheg

5

。しかし、キャッチは、cb(またはsize/length)フィールドが構造体の最初のものでなければならないということです。

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

あなたがクラスで構造体をラップしたい場合は、ラップしている構造が既にとして存在する可能性がありますので、私はあなたが最初のATL/WTLを試してみてくださいお勧めします。必要であればあなたはまた、単に拡張版を行うことができますそこのクラス。

独自のクラスを作成したい場合は、構造体のすべての要素を既定のパラメーターで指定したコンストラクターを作成し、後でこれらの値を変更しやすくすることをお勧めします。

+0

C++では、0は必要ありません。 – dalle

+0

小さなポイント:ゼロは必要ありません。 STARTUPINFO startup_info = {sizeof(STARTUPINFO)}; (残りの構造体は値が初期化されています) –

+1

本当ですか?あなたがしたくない場合は、コストを払わないという理念に従って、私は初期設定では初期化されていない構造にはゴミが入ると想定します。 私はゼロを明示的に指定します。 – Daemin

2

あなたは、テンプレートを使用することができます。

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

、その後:

{ 
    selfzero<STARTUPINFO> si; 
} 

警告:vtableのを持って以降のvtableを得たクラスまたは構造体の上にこれを使用し、それがしますgo bang。

3

私はtonjの提案を使用しましたが、それは多くの場合、私はこれをprefering終わったためにインテリセンス殺す以来:

<をselfzeroに比べ
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; 
} 

>これは、通常の場合には、別の行です:

STARTUPINFO si; 
ZeroInitCB(si); 

しかし - 言ったように - 私はインテリセンスを助けることを選んだ;)

はリターンT &は時々連鎖することができますが、私は使用していませんそれはしばしばです。

1

。多重継承、vtablesなどの場合でも安全です。 T構造体はメモリ内で連続して配置されなければならず、((T *)this)は他の基本クラスとvtableに対して適切に調整されます。

0

構造体の全ての非静的データメンバは、trivalコンストラクタを持っている場合あなたのクラスは、それらのデフォルト値にintializeメンバーに起こっているささいなコンストラクタを持っています。ほとんどの場合、目的に応じて構造体をゼロにすることとほとんど同じです。だから初期化のためにそれらをゼロにする "良い"練習かもしれないが、ほとんどの時間は必要ないだろう。

+0

組み込み型は、初期化子がユーザー定義コンストラクタ初期化子リストに明示的に含まれていない限り、デフォルトでは初期化されません。 WinAPIヘッダーの構造体には、ユーザー定義のコンストラクターがありません。 – sharptooth

+0

あなた自身を試してください: 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; }  
"P()"を出力します。 – piotr

関連する問題