使用 memset 将派生结构归零

Posted

技术标签:

【中文标题】使用 memset 将派生结构归零【英文标题】:zeroing derived struct using memset 【发布时间】:2011-08-16 17:55:13 【问题描述】:

我想将派生结构的所有成员归零。

有数百个成员,并且每隔一段时间就会添加更多成员,所以我觉得显式初始化它们很容易出错。

结构没有虚函数,所有成员字段都是内置的。但是,它们不是 POD,因为它们具有非平凡的构造函数。

除了对实践的标准皱眉之外,您是否发现以下任何问题?

struct Base

    // Stuff
;

struct Derived : public Base

    // Hundreds of fields of different built-in types
    // including arrays

    Derived()
    
        ::memset(reinterpret_cast<char*>this + sizeof (Base), 0, sizeof *this - sizeof (Base));
    
;

谢谢。

【问题讨论】:

【参考方案1】:

这假定Base 基类子对象位于Derived 的开头。如果您添加另一个基类,这将不起作用。

此外,您的代码是错误的:指针运算是根据对象执行的,而不是根据字节执行的。您需要使用reinterpret_cast&lt;char*&gt;(this) 以字节为单位执行算术运算。无论如何,你仍然不应该这样做。

考虑以下利用值初始化的、不难看的、符合标准的方法:

struct Derived : public Base

    struct DerivedMembers  /* ... */ 

    DerivedMembers data;

    Derived() : data()  
;

只要DerivedMembers 没有构造函数,这就会初始化data 的每个数据成员,这看起来正是您想要的行为。

或者,如果您希望在不使用“data”成员变量的情况下访问成员,请考虑使用另一个基类:

struct DerivedMembers  /* ... */ 

struct Derived : Base, DerivedMembers

    Derived() : DerivedMembers()  
;

【讨论】:

他不想避免对所有人这样做,因为他说这会“容易出错”吗?我知道没有其他方法可以做到这一点,尽管这是符合标准的,memset绝对不是解决这个问题的方法。 非常好的工作方式。但是我觉得如果一个类的成员变量太多,那就意味着你需要把这个类分开。 @Seth:这里我们只有一个数据成员data,这是初始化列表中唯一需要值初始化的数据成员。您可以向DerivedMembers 类添加任意数量的数据成员,它们都将被初始化,而无需在任何初始化代码中提及它们。 @James 他不会收到类似于non-aggregates cannot be initialized with initializer list 的错误,因为他说他的数据有(非平凡的)构造函数吗? @Seth:OP 说“所有成员字段都是 [of] 内置 [types]”,所以,不,应该没有问题。即使DerivedMembers 有一个类类型的数据成员,也会为该数据成员调用默认构造函数,或者如果它没有用户声明的构造函数,它的数据成员将被值初始化,递归(如果它没有默认值)构造函数,但有其他用户声明的构造函数,OP 的方案首先不起作用,因为他必须在初始化列表中对其进行初始化。【参考方案2】:

你不应该这样做。您应该在每个类中使用初始化列表,以避免必须这样做。第一遍就完成会很忙,但如果之后再继续练习,那就是微不足道的了。

看到这个similar question:

【讨论】:

【参考方案3】:

您应该将所有值显式设置为零并且不要使用 memset,因为它不可移植。编译器/内存分配可能存储了您可能正在覆盖的内务管理数据。

【讨论】:

【参考方案4】:

标准并没有“对实践不屑一顾”;它给出了未定义的行为

例如:

this + sizeof (Base)

C++ 标准中没有任何内容表明此表达式解析为指向 Derived 的指针。事实上,既然this 的类型是Derived * const,那么你所做的就是指针运算。 C++ 将尝试添加它,就好像this 是指向Derived 数组的指针,相当于this[sizeof(Base)]。这可能不是您想要的。

如果您不确定如何正确行事,请不要走在未定义行为的黑暗走廊中。

最重要的是,即使您将体操指针指向实际工作,您的代码也会变得非常脆弱。它可能适用于您的编译器的这个版本,但它会在以后的版本中失败。做一些简单的事情,比如向Base 添加一个虚函数会在你的代码中造成混乱,因为你会破坏 vtable 指针。

在我看来你有几个问题:

    不必要的推导。如果Base 中没有任何虚拟内容,您为什么要公开从它派生? 胖接口。如果您看到一个班级开始有 数百 个成员,那么它可能做得太多了。

【讨论】:

是的,我在编写代码时就意识到了这一点,但不幸的是,在发布到 SO 时忘记了演员阵容。现已修复。

以上是关于使用 memset 将派生结构归零的主要内容,如果未能解决你的问题,请参考以下文章

在c中将结构的内存区域归零[重复]

在 C 中将二维数组归零的最快方法是啥?

我需要在 Swift 中 memset 一个 C 结构吗?

为啥我们在套接字编程c中使用memset将结构填充为0?

使用 memset 将结构体数组及其成员初始化为零

如何在 C 中 memset 结构