2009-04-22 59 views
3

Win32編程中使用了大量的結構。很多時候,只有一些字段被使用,其他所有字段都被設置爲零。例如:清零構造函數中的struct

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 

我想停止複製粘貼這樣的代碼,而是使用一個抽象,將關心歸零和設置參數。假設我只需要像在示例中那樣初始化結構,並且不需要其他調整。以下是一個很好的解決方案嗎?什麼是可能的問題?

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

我特別關心的ZeroMemory()調用 - 看起來像我完全控制代碼和類沒有虛函數表,並調用ZeroMemory()這個方法是安全的,有兩者之間沒有大的區別代碼片段除了後者提供抽象。是否有任何警告?

回答

1

我認爲這是使這種結構更加防彈的好方法。我不確定爲什麼其他人似乎不喜歡這種技術。我偶爾會使用它,但不會像其他情況那樣頻繁,因爲出於某種原因,它似乎不會被同事非常喜歡。

我沒有看到它在公佈的材料經常使用 - 這是唯一一個我可以在一個快速谷歌發現現在的問題是在MSJ 1997年8月(http://www.microsoft.com/MSJ/0897/C0897.aspx)的作者簡介Paul DiLascia的文章:

CRebarInfoCRebarBandInfo是程序員友好的C++版本的C結構REBARINFOREBARBANDINFO,其構造函數在將對象初始化爲全零之前,適當地設置cbSize成員。

我想不出太多的缺點(除了缺乏接受)。如果其他人可以指出更具體的東西,我會很感激。

4

而不是子類化,爲什麼不創建一個函數?

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

這是真的,這可以把一個不平凡的大小的結構放在堆棧上。如果有問題的編譯器確實命名了返回值優化(link),則只會創建一個副本。但無論如何,你的例子你已經有過一次實例並暫時放一秒鐘,這不太可能造成重大問題。

如果在結構中存在未正確管理的資源,我通常只會繼承一個結構的子類。通常是實施RAII模型。在您的特定示例中,沒有額外的資源管理正在發生,因此我將避免使用子類並只使用一個函數。

+1

好的解決方案。爲什麼?因爲編譯器會通過NRVO完全優化它! *沒有第二個副本*的結構將在堆棧上創建,不會複製返回值(如果上面的代碼在初始化中使用)。該功能簡單而純粹地具有零開銷。 – 2009-04-22 12:25:12

+1

@Konrad,啊是的,我忘了NRVO:http://msdn.microsoft.com/en-us/library/ms364057.aspx – JaredPar 2009-04-22 14:41:06

-1

您可以創建行爲類似普通類型的各類特殊包裝,但以零初始化,如:

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

然後使用這些類型:

struct MyStruct { 
    zbool deleted; 
}; 

當然,這不會如果您嘗試初始化外部結構,則工作。

+2

-1你不能從C++的內置類型派生。 – 2009-04-22 08:07:17

+0

是的,我不能。對不起,我沒有用C++編寫多年,但你有想法。 – stepancheg 2009-04-22 19:34:12

5

對於結構可以這樣做:

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

我覺得這是一個巧妙的方法來初始化這些類型的結構。然而問題在於cb(或大小/長度)字段必須是結構中的第一個字段。你也可以只做到擴展版本如果需要的話:

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

如果你想包裝結構與一類我建議你先嚐試ATL/WTL,因爲你包裹可能已存在的結構在那裏上課。

如果您仍然熱衷於創建自己的類,我建議您創建一個構造函數,該函數按順序接收結構中的每個元素,並指定默認參數,以便稍後更改這些值。

+0

在C++中,你甚至不需要0. – dalle 2009-04-22 08:10:20

2

您可以使用模板:

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

然後:

{ 
    selfzero<STARTUPINFO> si; 
} 

的警告:在類或結構有虛表或有一個虛函數表後使用,它會砰的一聲。

3

我用通季的建議,但由於它殺死IntelliSense來的時候,我結束了prefering這樣的:

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; 
} 

相比selfzero <>這是另一條線在正常情況下:

STARTUPINFO si; 
ZeroInitCB(si); 

但如 - 說 - 我選擇幫助intellisense;)

返回T &有時允許鏈接,但我沒有使用它經常。

1

爲了提高通季的解決方案:

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

零點T,不selfdata。即使在多重繼承,vtables等情況下也很安全。 T結構必須在內存中連續分配,並且((T *)this)適用於其他基類和vtable。

0

如果所有結構的非靜態數據成員有繁瑣的構造你的類有打算intialize會員爲其默認值一個簡單的構造函數。大多數時候,完全相同的目的是零結構的結構。所以雖然可能是「好」的做法,將它們歸零以進行初始化,但大部分時間不會有任何需要。