zeroing derived struct using memset
Asked Answered
F

4

1

I want to zero out all members of a derived structure.

There are hundreds of members and more are added every once in a while so I feel that initializing them explicitly is error-prone.

The structures have no virtual functions and all the member fields are built-in. However, they are not POD by virtue of having non-trivial constructors.

Apart from the standard frowning on the practice, do you see any issues with the following?

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));
    }
};

Thanks.

Foal answered 16/8, 2011 at 17:55 Comment(0)
P
8

This assumes that the Base base class subobject is located at the beginning of Derived. This won't work if you add another base class.

Further, your code is wrong: pointer arithmetic is performed in terms of objects, not in terms of bytes. You need to use reinterpret_cast<char*>(this) to perform the arithmetic in terms of bytes. In any case, you still shouldn't do this.

Consider the following, non-ugly, standards-conforming approach utilizing value initialization:

struct Derived : public Base
{
    struct DerivedMembers { /* ... */ }

    DerivedMembers data;

    Derived() : data() { }
};

As long as DerivedMembers has no constructor, this will value initialize each of the data members of data, which look like it's exactly the behavior you want.

Or, if you want the members to be accessible without using a "data" member variable, consider using another base class:

struct DerivedMembers { /* ... */ }

struct Derived : Base, DerivedMembers
{
    Derived() : DerivedMembers() { }
};
Proterozoic answered 16/8, 2011 at 17:58 Comment(7)
Didn't he want to avoid doing that to all of them because he said it would be "error prone"? I know there's not really another way to do it though that is standards-conformant, and memset is definitely not the solution to this problem.Giffy
really good way of doing the job. but I feel if a class has too many member variables, that means you need to separate the class.Vast
@Seth: Here we only have a single data member, data, and that is the only data member that needs to be value-initialized in the initialization list. You can add as many data members as you'd like to the DerivedMembers class and they'll all be value initialized, without having to mention them in any initialization code.Proterozoic
@James won't he get an error that goes something like non-aggregates cannot be initialized with initializer list because he said his data has (non-trivial) constructors?Giffy
@Seth: The OP says "all the member fields are [of] built-in [types]," so, no, there should be no problem. Even if DerivedMembers has a class-type data member, the default constructor will be called for that data member, or if it has no user-declared constructors, its data members will be value-initialized, recursively (if it has no default constructor but has other user-declared constructors, the OP's scheme wouldn't work in the first place because he would have to initialize it in the initialization list).Proterozoic
@James ok, coolio. I must have misread that as the member fields having non-trivial constructors (which would mean they weren't of built in types).Giffy
James, the multiple inheritance example is elegant and portable. I cannot use it due to internal coding standards prohibiting multiple inheritance but I'll still accept it as the answer.Foal
K
2

You shouldn't do this. You should use initialization lists in each of your classes to avoid the necessity of having to do this. It will be a lot of busy work to get it done on the first pass, but if after that the practice is followed it's trivial.

See this similar question:

Karlkarla answered 16/8, 2011 at 18:0 Comment(0)
P
2

You should explicitly set all the values to zero and not use memset as this is not portable. The compiler/memory allocation may have housekeeping data stored that you may be overwriting.

Protractor answered 16/8, 2011 at 18:0 Comment(0)
A
2

The standard does not "frown on the practice"; it gives undefined behavior.

For example:

this + sizeof (Base)

There is nothing in the C++ standard that says that this expression resolves to a pointer to Derived. Indeed, since the type of this is Derived * const, then what you've done is pointer arithmetic. C++ will try to add to it as though this were a pointer to an array of Derived, the equivalent of this[sizeof(Base)]. Which probably isn't what you wanted.

Do not walk the dark corridors of undefined behavior if you're not sure how to do it right.

Most importantly of all, even if you pointer gymnastics to actually work, your code becomes very fragile. It may work on this version of your compiler, but it will fail on a later one. Doing something simple like adding a virtual function to Base will cause chaos in your code, as you will destroy the vtable pointer.

It seems to me that you have a couple of problems:

  1. Needless derivation. If Base has nothing virtual in it, why are you publicly deriving from it?
  2. A fat interface. If you see a class start to have hundreds of members, then it's probably doing too much.
Amateurism answered 16/8, 2011 at 18:8 Comment(1)
Yes, I was aware of that when I wrote the code but unfortunately forgot about the cast when posting to SO. Fixed now.Foal

© 2022 - 2024 — McMap. All rights reserved.