memset for initialization in C++
Asked Answered
C

2

20

memset is sometimes used to initialize data in a constructor like the example below. Does it work in general ? Is it a good idea in general?

class A {
public:
   A();
private:
   int a;
   float f;
   char str[35];
   long *lp;
};

A::A()
{
   memset(this, 0, sizeof(*this));
}
Cosset answered 20/3, 2010 at 2:21 Comment(1)
You can use the following approach: https://mcmap.net/q/245573/-how-to-do-the-equivalent-of-memset-this-without-clobbering-the-vtblFikes
T
27

Don't use memset. It's a holdover from C and won't work on non-PODs. Specifically, using it on a derived class that contains any virtual functions -- or any class containing a non-builtin -- will result in disaster.

C++ provides a specific syntax for initialization:

class A {
public:
   A();
private:
   int a;
   float f;
   char str[35];
   long *lp;
};

A::A()
    : a(0), f(0), str(), lp(NULL)
{
}

To be honest, I'm not sure, but memset might also be a bad idea on floating-points since their format is unspecified.

Thoreau answered 20/3, 2010 at 2:28 Comment(4)
There's also std::fill or std::fill_n if you need to "memset" an array (or any other sequence)Jarid
For the array case it's better to do str(). GCC has problems initializing the char array by a string literal through a member-initializer, and if the array is not a char array it won't work even in a standard conforming compiler. str() will always nullify the array. (i just submitted a bug report: gcc.gnu.org/bugzilla/show_bug.cgi?id=43453 )Rachitis
@litb: Noted and changed above.Thoreau
For C++11 I would add = {} for each field definition godbolt.org/z/91WfajrjnAdenectomy
G
17

It's a terrible idea. You're just tromping over data, paying no heed to how objects should be initialized. If your class is virtual, you're likely to wipe out the vtable pointer as well.

memset works on raw data, but C++ isn't about raw data. C++ creates abstractions, so if you want to be safe you use those abstractions. Use the initializer list to initialize members.

You can do it to POD types:

struct nothing_fancy_here
{
    bool b;
    int i;
    void* p;
};

nothing_fancy_here x;
memset(&x, 0, sizeof(x));

But if you're doing it on this, that means you're in a user-defined constructor and no longer qualify as a POD type. (Though if all your members are POD it might work, as long as none contain 0 as a trap value. I'm sure not sure if any other sources of undefined behavior come into play here.)

Gallinule answered 20/3, 2010 at 2:26 Comment(11)
Doesn't your nothing_fancy_here type also have a constructor? I don't think there's any difference between what you are calling a POD type, and the OPs example class... Whether you are calling memset from within the constructor or not is irrelevant. If it's ok in your example, it's ok in the OP's.Kettledrummer
@STingRay: It has no constructor. Maybe the // ... is too open.Gallinule
It does have a constructor. Granted it's a no-op. The point about whether there is an explicitly-declared constructor or not is irrelevant. What matters is, as you said, whether the struct/class (or any of its members' types) has virtual member functions. If the OP hadn't declared a constructor and called memset just as you did, there is no difference. So the ultimate answer is: in the given examples, it is technically ok to do, but as a general practice, certainly not...Kettledrummer
@STingRay: Sorry, I meant user-defined constructor. Having a user-defined constructor, like the OP has, means it's not an aggregate and therefore not a POD. Note I'm calling memset outside the class. Calling it in the constructor automatically means it's not a POD type, because I've defined a constructor.Gallinule
<offtopic> Thanks for keeping that other question alive. It's a bit more complicated than I had hoped.Kettledrummer
@GMan: I still argue that whether or not a user-defined constructor exists is irrelevant. Why does having a user-defined constructor pose risk in using the memset construct? I believe the only valid concern is overwriting the vtable pointer, but even that must depend on implementation-defined behavior (depending on exactly when the vtable pointer is initialized). Of course the same concern applies recursively to member data of user-defined types...Kettledrummer
@STingRay: I agree it's a bit gray. The thing is, it is undefined behavior to call memset on a non-POD, I think we agree. So if I do it on a std::string be it a member or not, the result is undefined. If we have a struct that contains non-POD members and we memset that struct, we have undefined behavior. Now consider a class with only POD members, but is not an aggregate (has a user defined constructor). By definition, it is not a POD now, so even though all of it's members are POD, it itself is not so we cannot have a well-defined memset. Considering that, it makes no difference..Gallinule
..if that memset call was external from the class or internal (like the constructor). The point remains, with a constructor it's not POD and cannot be safely memset'd. Practically, it'll probably be fine, but technically speaking it's not.Gallinule
@GMan: So, the standard explicitly states that using memset to initialize the memory occupied by a non-POD type is UB? (My copy of the standard is in the mail.)Kettledrummer
@STingRaySC: No, there are dots to connect, but they are there. That said, I can't find them right now. :P However, the strings library section (where memset is defined) starts with: "This clause describes components for manipulating sequences of "characters,” where characters may be of any POD (3.9) type." It pretty much comes down to: non-POD cannot be treated as a contiguous sequence of bits.Gallinule
GManNickG you find a compiler which return different result between sizeof(x) and sizeof(*this) ?Kazan

© 2022 - 2024 — McMap. All rights reserved.