Question

Instead of having to remember to initialize a simple 'C' structure, I might derive from it and zero it in the constructor like this:

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct()
    {
        memset(this, 0, sizeof(MY_STRUCT));
    }
};

This trick is often used to initialize Win32 structures and can sometimes set the ubiquitous cbSize member.

Now, as long as there isn't a virtual function table for the memset call to destroy, is this a safe practice?

Was it helpful?

Solution

PREAMBLE:

While my answer is still Ok, I find litb's answer quite superior to mine because:

  1. It teaches me a trick that I did not know (litb's answers usually have this effect, but this is the first time I write it down)
  2. It answers exactly the question (that is, initializing the original struct's part to zero)

So please, consider litb's answer before mine. In fact, I suggest the question's author to consider litb's answer as the right one.

Original answer

Putting a true object (i.e. std::string) etc. inside will break, because the true object will be initialized before the memset, and then, overwritten by zeroes.

Using the initialization list doesn't work for g++ (I'm surprised...). Initialize it instead in the CMyStruct constructor body. It will be C++ friendly:

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct() { n1 = 0 ; n2 = 0 ; }
};

P.S.: I assumed you did have no control over MY_STRUCT, of course. With control, you would have added the constructor directly inside MY_STRUCT and forgotten about inheritance. Note that you can add non-virtual methods to a C-like struct, and still have it behave as a struct.

EDIT: Added missing parenthesis, after Lou Franco's comment. Thanks!

EDIT 2 : I tried the code on g++, and for some reason, using the initialization list does not work. I corrected the code using the body constructor. The solution is still valid, though.

Please reevaluate my post, as the original code was changed (see changelog for more info).

EDIT 3 : After reading Rob's comment, I guess he has a point worthy of discussion: "Agreed, but this could be an enormous Win32 structure which may change with a new SDK, so a memset is future proof."

I disagree: Knowing Microsoft, it won't change because of their need for perfect backward compatibility. They will create instead an extended MY_STRUCTEx struct with the same initial layout as MY_STRUCT, with additionnal members at the end, and recognizable through a "size" member variable like the struct used for a RegisterWindow, IIRC.

So the only valid point remaining from Rob's comment is the "enormous" struct. In this case, perhaps a memset is more convenient, but you will have to make MY_STRUCT a variable member of CMyStruct instead of inheriting from it.

I see another hack, but I guess this would break because of possible struct alignment problem.

EDIT 4: Please take a look at Frank Krueger's solution. I can't promise it's portable (I guess it is), but it is still interesting from a technical viewpoint because it shows one case where, in C++, the "this" pointer "address" moves from its base class to its inherited class.

OTHER TIPS

You can simply value-initialize the base, and all its members will be zero'ed out. This is guaranteed

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct():MY_STRUCT() { }
};

For this to work, there should be no user declared constructor in the base class, like in your example.

No nasty memset for that. It's not guaranteed that memset works in your code, even though it should work in practice.

Much better than a memset, you can use this little trick instead:

MY_STRUCT foo = { 0 };

This will initialize all members to 0 (or their default value iirc), no need to specifiy a value for each.

This would make me feel much safer as it should work even if there is a vtable (or the compiler will scream).

memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

I'm sure your solution will work, but I doubt there are any guarantees to be made when mixing memset and classes.

This is a perfect example of porting a C idiom to C++ (and why it might not always work...)

The problem you will have with using memset is that in C++, a struct and a class are exactly the same thing except that by default, a struct has public visibility and a class has private visibility.

Thus, what if later on, some well meaning programmer changes MY_STRUCT like so:


struct MY_STRUCT
{
    int n1;
    int n2;

   // Provide a default implementation...
   virtual int add() {return n1 + n2;}  
};

By adding that single function, your memset might now cause havoc. There is a detailed discussion in comp.lang.c+

The examples have "unspecified behaviour".

For a non-POD, the order by which the compiler lays out an object (all bases classes and members) is unspecified (ISO C++ 10/3). Consider the following:

struct A {
  int i;
};

class B : public A {       // 'B' is not a POD
public:
  B ();

private:
  int j;
};

This can be laid out as:

[ int i ][ int j ]

Or as:

[ int j ][ int i ]

Therefore, using memset directly on the address of 'this' is very much unspecified behaviour. One of the answers above, at first glance looks to be safer:

 memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

I believe, however, that strictly speaking this too results in unspecified behaviour. I cannot find the normative text, however the note in 10/5 says: "A base class subobject may have a layout (3.7) different from the layout of a most derived object of the same type".

As a result, I compiler could perform space optimizations with the different members:

struct A {
  char c1;
};

struct B {
  char c2;
  char c3;
  char c4;
  int i;
};

class C : public A, public B
{
public:
  C () 
  :  c1 (10);
  {
    memset(static_cast<B*>(this), 0, sizeof(B));      
  }
};

Can be laid out as:

[ char c1 ] [ char c2, char c3, char c4, int i ]

On a 32 bit system, due to alighments etc. for 'B', sizeof(B) will most likely be 8 bytes. However, sizeof(C) can also be '8' bytes if the compiler packs the data members. Therefore the call to memset might overwrite the value given to 'c1'.

Precise layout of a class or structure is not guaranteed in C++, which is why you should not make assumptions about the size of it from the outside (that means if you're not a compiler).

Probably it works, until you find a compiler on which it doesn't, or you throw some vtable into the mix.

If you already have a constructor, why not just initialize it there with n1=0; n2=0; -- that's certainly the more normal way.

Edit: Actually, as paercebal has shown, ctor initialization is even better.

My opinion is no. I'm not sure what it gains either.

As your definition of CMyStruct changes and you add/delete members, this can lead to bugs. Easily.

Create a constructor for CMyStruct that takes a MyStruct has a parameter.

CMyStruct::CMyStruct(MyStruct &)

Or something of that sought. You can then initialize a public or private 'MyStruct' member.

From an ISO C++ viewpoint, there are two issues:

(1) Is the object a POD? The acronym stands for Plain Old Data, and the standard enumerates what you can't have in a POD (Wikipedia has a good summary). If it's not a POD, you can't memset it.

(2) Are there members for which all-bits-zero is invalid ? On Windows and Unix, the NULL pointer is all bits zero; it need not be. Floating point 0 has all bits zero in IEEE754, which is quite common, and on x86.

Frank Kruegers tip addresses your concerns by restricting the memset to the POD base of the non-POD class.

Try this - overload new.

EDIT: I should add - This is safe because the memory is zeroed before any constructors are called. Big flaw - only works if object is dynamically allocated.

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct()
    {
        // whatever
    }
    void* new(size_t size)
    {
        // dangerous
        return memset(malloc(size),0,size);
        // better
        if (void *p = malloc(size))
        {
            return (memset(p, 0, size));
        }
        else
        {
            throw bad_alloc();
        }
    }
    void delete(void *p, size_t size)
    {
        free(p);
    }

};

If MY_STRUCT is your code, and you are happy using a C++ compiler, you can put the constructor there without wrapping in a class:

struct MY_STRUCT
{
  int n1;
  int n2;
  MY_STRUCT(): n1(0), n2(0) {}
};

I'm not sure about efficiency, but I hate doing tricks when you haven't proved efficiency is needed.

Comment on litb's answer (seems I'm not yet allowed to comment directly):

Even with this nice C++-style solution you have to be very careful that you don't apply this naively to a struct containing a non-POD member.

Some compilers then don't initialize correctly anymore.

See this answer to a similar question. I personally had the bad experience on VC2008 with an additional std::string.

What I do is use aggregate initialization, but only specifying initializers for members I care about, e.g:

STARTUPINFO si = {
    sizeof si,      /*cb*/
    0,              /*lpReserved*/
    0,              /*lpDesktop*/
    "my window"     /*lpTitle*/
};

The remaining members will be initialized to zeros of the appropriate type (as in Drealmer's post). Here, you are trusting Microsoft not to gratuitously break compatibility by adding new structure members in the middle (a reasonable assumption). This solution strikes me as optimal - one statement, no classes, no memset, no assumptions about the internal representation of floating point zero or null pointers.

I think the hacks involving inheritance are horrible style. Public inheritance means IS-A to most readers. Note also that you're inheriting from a class which isn't designed to be a base. As there's no virtual destructor, clients who delete a derived class instance through a pointer to base will invoke undefined behaviour.

I assume the structure is provided to you and cannot be modified. If you can change the structure, then the obvious solution is adding a constructor.

Don't over engineer your code with C++ wrappers when all you want is a simple macro to initialise your structure.

#include <stdio.h>

#define MY_STRUCT(x) MY_STRUCT x = {0}

struct MY_STRUCT
{
    int n1;
    int n2;
};

int main(int argc, char *argv[])
{
    MY_STRUCT(s);

    printf("n1(%d),n2(%d)\n", s.n1, s.n2);

    return 0;
}

It's a bit of code, but it's reusable; include it once and it should work for any POD. You can pass an instance of this class to any function expecting a MY_STRUCT, or use the GetPointer function to pass it into a function that will modify the structure.

template <typename STR>
class CStructWrapper
{
private:
    STR MyStruct;

public:
    CStructWrapper() { STR temp = {}; MyStruct = temp;}
    CStructWrapper(const STR &myStruct) : MyStruct(myStruct) {}

    operator STR &() { return MyStruct; }
    operator const STR &() const { return MyStruct; }

    STR *GetPointer() { return &MyStruct; }
};

CStructWrapper<MY_STRUCT> myStruct;
CStructWrapper<ANOTHER_STRUCT> anotherStruct;

This way, you don't have to worry about whether NULLs are all 0, or floating point representations. As long as STR is a simple aggregate type, things will work. When STR is not a simple aggregate type, you'll get a compile-time error, so you won't have to worry about accidentally misusing this. Also, if the type contains something more complex, as long as it has a default constructor, you're ok:

struct MY_STRUCT2
{
    int n1;
    std::string s1;
};

CStructWrapper<MY_STRUCT2> myStruct2; // n1 is set to 0, s1 is set to "";

On the downside, it's slower since you're making an extra temporary copy, and the compiler will assign each member to 0 individually, instead of one memset.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top