2009-04-22 57 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会员为其默认值一个简单的构造函数。大多数时候,完全相同的目的是零结构的结构。所以虽然可能是“好”的做法,将它们归零以进行初始化,但大部分时间不会有任何需要。