将构造函数中的结构清零

时间:2009-04-22 06:13:12

标签: c++ winapi

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()这种方式是安全的并且两个代码片段之间没有大的区别,除了后者提供了抽象。有什么警告吗?

8 个答案:

答案 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):

  

CRebarInfoCRebarBandInfo是C语言结构REBARINFOREBARBANDINFO的程序员友好型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;
};

当然,如果您尝试初始化外部结构,这将无效。