Win32编程中使用了各种结构。很多时候只使用它们的一些字段而所有其他字段都设置为零。例如:
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()调用 - 看起来我完全控制代码并且类没有vtable并且调用ZeroMemory()这种方式是安全的并且两个代码片段之间没有大的区别,除了后者提供了抽象。有什么警告吗?
答案 0 :(得分: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,因为你要包装的结构可能已经作为类存在了。
如果您仍然热衷于创建自己的类,我建议您创建一个构造函数,按顺序获取结构的每个元素,并指定默认参数,以便以后更容易更改这些值。
答案 1 :(得分:4)
为什么不创建一个函数而不是子类?
STARTUPINFO CreateStartupInfo( DWORD flags ) {
STARTUPINFO info;
ZeroMemory(&info, sizeof(info));
info.cb = sizeof(STARTUPINFO);
info.dwFlags = flags;
return info;
}
如果这可以在堆栈上放置一个非平凡大小的结构。如果有问题的编译器执行了命名返回值优化(link),则只会创建一个副本。但无论如何,你已经拥有一次实例而暂时放一秒的例子不太可能引起重大问题。
如果在结构中没有正确管理资源,我通常只会对结构进行子类化。通常,它是实现RAII模型。在您的特定示例中,没有进行额外的资源管理,因此我将避免使用子类并使用函数。
答案 2 :(得分:3)
我已经使用了tonj的建议,但由于它经常杀死intellisense,我最后更喜欢这个:
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&lt;&gt;相比这是正常情况下的另一条线:
STARTUPINFO si;
ZeroInitCB(si);
但是 - 如上所述 - 我选择帮助intellisense;)
返回T&amp;有时允许链接,但我不经常使用它。
答案 3 :(得分:2)
您可以使用模板:
template <class T>
class selfzero : public T
{
public:
selfzero() {
ZeroMemory( this, sizeof( selfzero<T> ));
};
};
然后:
{
selfzero<STARTUPINFO> si;
}
警告:在具有vtable或稍后获得vtable的类或结构上使用它,它将会爆炸。
答案 4 :(得分:1)
我认为这是使这种结构更加防弹的好方法。我不确定为什么其他人似乎不喜欢这种技术。我偶尔会使用它,但不会像我一样经常使用它,因为出于某种原因,同事似乎不太喜欢它。
我没有经常在出版材料中看到它 - 我现在在快速Google中找到的唯一一篇是Paul DiLascia在1997年8月的MSJ上发表的一篇文章(http://www.microsoft.com/MSJ/0897/C0897.aspx):
CRebarInfo
和CRebarBandInfo
是C语言结构REBARINFO
和REBARBANDINFO
的程序员友好型C ++版本,其中构造函数在设置{{{{}}之前将对象初始化为全零。 1}}成员适当。
我不能想到很多缺点(除了缺乏接受)。如果其他人可以指出更具体的东西,我会很感激。
答案 5 :(得分:1)
改进TonJ的解决方案:
template <class T>
class selfzero : public T
{
public:
selfzero() {
ZeroMemory( (T*) this, sizeof( T ));
};
};
Zeroes T,不是selfdata。在多重继承,vtable等的情况下甚至是安全的。 T结构必须在内存中连续分配,并且((T *)this)可以适当调整其他基类和vtable。
答案 6 :(得分:0)
如果结构的所有非静态数据成员都具有 trival构造函数,则您的类具有一个简单的构造函数,该构造函数将将成员初始化为其默认值。大部分时间与故意将结构归零完全相同。因此,虽然将初始化归零可能是“好”的做法,但大部分时间都没有任何需要。
答案 7 :(得分:-1)
您可以创建类型的特殊包装,其行为类似普通类型,但使用零初始化,如:
class zbool {
private:
bool value;
public:
zbool(const bool value) { ... }
operator bool() { ... }
// ... code skipped
};
然后使用这些类型:
struct MyStruct {
zbool deleted;
};
当然,如果您尝试初始化外部结构,这将无效。