代码之家  ›  专栏  ›  技术社区  ›  sharptooth

在构造函数中清空结构

  •  3
  • sharptooth  · 技术社区  · 16 年前

    在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 回复  |  直到 16 年前
        1
  •  1
  •   Michael Burr    16 年前

    我认为这是使这种结构更加防弹的好方法。我不知道为什么其他人似乎不喜欢这种技术。我偶尔使用它,但并不像其他时候那样频繁,因为出于某种原因,它似乎不太受同事喜欢。

    我不经常看到它在已发表的材料中被使用-我现在能在一个快速的谷歌中找到的唯一一篇文章是由PaulDilassia于1997年8月在MSJ发表的一篇文章。( http://www.microsoft.com/MSJ/0897/C0897.aspx ):

    CRebarInfo CRebarBandInfo 是程序员友好的C++结构的C++结构 REBARINFO REBARBANDINFO ,其中的构造函数在设置 cbSize 适当的成员。

    我想不出多少缺点(除了缺乏接受)。如果有人能指出更具体的事情,我会感激的。

        2
  •  5
  •   Dominik Grabiec    16 年前

    对于结构,可以执行以下操作:

    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,因为您包装的结构可能已经作为类存在于那里。

    如果您仍然热衷于创建自己的类,那么我建议您创建一个构造函数,它将结构的每个元素按顺序排列,并指定默认参数,这样以后更容易更改这些值。

        3
  •  4
  •   sharptooth    16 年前

    为什么不创建一个函数而不是子类化呢?

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

    如果为true,则可以在堆栈上放置一个非平凡大小的结构。如果有问题的编译器执行命名的返回值优化( link )将只创建一个副本。但在任何情况下,您的示例已经有一个实例,并且临时放置一个实例,不太可能导致重大问题。

    如果结构中存在未正确管理的资源,我通常只对结构进行子类化。通常是实现RAII模型。在您的特定示例中,没有发生额外的资源管理,因此我将避免使用子类,只使用一个函数。

        4
  •  3
  •   peterchen    16 年前

    我已经使用了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相比,它是正常情况下的另一行:

    STARTUPINFO si;
    ZeroInitCB(si);
    

    但正如所说,我选择了帮助智能感知;

    返回T&有时允许链接,但我不经常使用它。

        5
  •  2
  •   TonJ    16 年前

    您可以使用模板:

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

    然后:

    {
        selfzero<STARTUPINFO> si;
    }
    

    警告:在具有vtable或稍后获得vtable的类或结构上使用此选项,它将爆炸。

        6
  •  1
  •   MSalters    16 年前

    要改进Tonj的解决方案:

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

    零是t,不是selfdata。即使在多重继承、vtables等情况下也是安全的。T结构必须在内存中连续分配,并且((T*)这对于其他基类和vtables是适当调整的。

        7
  •  0
  •   piotr    16 年前

    如果结构的所有非静态数据成员都具有 三位一体的建设者 您的类有一个将要 将成员初始化为其默认值 .大多数情况下,这与故意调零结构完全相同。因此,虽然在初始化时将它们归零可能是“好”的实践,但大多数时候不需要。

        8
  •  -1
  •   stepancheg    16 年前

    您可以创建一些类型的特殊包装器,这些类型的行为类似于普通类型,但用零初始化,例如:

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

    然后使用这些类型:

    struct MyStruct {
      zbool deleted;
    };
    

    当然,如果您试图初始化外部结构,这将不起作用。