Sign in to follow this  
suliman

set all members to zero?

Recommended Posts

suliman    1653
hi is there any way to set all members (ints, float, pointers etc) to zero for a class without making a function for it? So that when i add new members they will automatically be effected by this "reset". Like: class myClass{ int pop; float pop2; unit * pop3; } //like so void myClass::reset(){ memResetWhatever(); } //not simply doing it by hand: void myClass::reset(){ pop=0; pop2=0; pop3=0; } When you start to have 30 something members of a class and all of them should be set to zero att startup you start to wish for somehting that can do it for you... thanks erik

Share this post


Link to post
Share on other sites
SunTzu    286
Not for a class, no. More specifically, not for anything that is not POD (Plain Old Data).

For something that is POD - like a typical struct - you can do something like this:

struct MyStruct
{
// ... member
}

MyStruct a = { 0 };

Alternatively you could do memset() or ZeroMemory().

However, most classes - and definitely any class that is derived from another class - contain other data than just their members, such as vtable pointers and so forth. I'm sure that someone's going to pick holes and say "But not all classes..." or "But some structs..." or whatever - the point is, if it's POD you can zero it, if it's not you can't, but if you think something's POD, zero it, and it turns out NOT to be POD, the world will end. Or at least your program will, nastily. So don't do it.

Apart from anything else, zero is a number like any other. You're just as likely to want to assign/initialise a variable to 1.0f or something. So I'm afraid explicit assignment is the only way to do it.

Share this post


Link to post
Share on other sites
password    181
What you're asking for can probably be done with a constructor, this won't do it automatically though. Every time you make an instance of the class all the values will be set to 0 however.


class myClass {
public:
myClass() { pop=pop2=pop3=0; } // inline constructor

private:
int pop;
float pop2;
unit * pop3;
};

myClass class1; // this class's members will automatically have the value 0

Share this post


Link to post
Share on other sites
dmail    116
Quote:
all of them should be set to zero att startup

This is the job of the constructor, to put the class into a stable usable state. If you start to have thirty member variables maybe your design(doesn't sound like you design as you say "when i add new members") needs to be rethought.

If you wanted to reset the class after construction then supply a assignment operator (if a deep copy is required) and create a new class of the type and assign it to the already constructed class.

edit too slow :)

Share this post


Link to post
Share on other sites
Numsgil    501
I've had some experience with this too. I had a class in a large project that was constantly getting new members added. When you have 50+ members, it gets really old to set them all to zero. And you have to worry about forgetting to set any new members you add in the umpteen different places that you're setting all the values (if you forget to set the value somewhere, 0 tends to be safer than some random garbage).

What I finally did was memset(&myclassinstance, 0x00, sizeof(myclassinstance));

This isn't correct. It's behavior seems to be compiler dependant (I had success with MSVC6 but devc++ would explode ;)). However, if it doesn't work, it'll be very evident very early on. It won't cause any phantom bugs that I ever discovered.

In short, a memset is a quick and dirty hack that's poor practice if it works at all. But feel free to use it if it works on your platform and you realize that it's poor practice, and comment it as such. No coding police are going to arrest you.

Replace it with a proper constructor when your class starts to stabilize (you stop adding new members), or if you want to distribute a binary (I'm not sure how the exe will behave on other computers from your own.)

Share this post


Link to post
Share on other sites
jpetrie    13138
Quote:

I've had some experience with this too. I had a class in a large project that was constantly getting new members added. When you have 50+ members, it gets really old to set them all to zero. And you have to worry about forgetting to set any new members you add in the umpteen different places that you're setting all the values (if you forget to set the value somewhere, 0 tends to be safer than some random garbage).


This still smacks of terrible design, and for more than the obvious "too many members" reason -- you've also got the "internal" design of the class flawed because there are "umpteeen" different places to set variables. Fixing the design would still be the optimal solution here.

Quote:

However, if it doesn't work, it'll be very evident very early on. It won't cause any phantom bugs that I ever discovered.


It will cause phantom bugs when you add something to the class that requires specific values to be located in a specific region of memory within the region that is determined by sizeof(classtype) -- the most common example of such is the virtual table pointer, which many implementations will place in the first sizeof(pointer) bytes of an instance.

The argument that you just won't add virtual methods or any such, commonly used to counteract what I just said, doesn't tend to hold water because as you already pointed out, it can be hard to remember something about a class this big.

I am strongly opposed to doing anything that you know to be a bad idea, no matter how much it seems like a good idea (or that you won't forget to fix it later) at the time; doing stupid things intentionally is just that -- stupid.

Besides the poor design, there are more subtle (and admittedly more pedantic) gotchas:

  • You cannot reliably store pointers as members of the class (while a constant integral value of zero will be converted to the null pointer value, the value represention of a null pointer is not necessarily zero, and by using memset() you modify the value representation directly).

  • You cannot reliably store floating-pointer members (the value representation of floats does not need to be one where 0.0 happens to correspond with an all-zero bitpattern).

  • You cannot store any class type that cannot be reliably "zeroed out" via this same ugly technique.



And so on. Just say no to memset, and fix your design.

Share this post


Link to post
Share on other sites
vicviper    130
Quote:
Original post by Numsgil
I've had some experience with this too. I had a class in a large project that was constantly getting new members added. When you have 50+ members, it gets really old to set them all to zero. And you have to worry about forgetting to set any new members you add in the umpteen different places that you're setting all the values (if you forget to set the value somewhere, 0 tends to be safer than some random garbage).

What I finally did was memset(&myclassinstance, 0x00, sizeof(myclassinstance));



using a memset on a class is highly dangerous, because if that class has virtual methods, they are cleared too, and that usually causes nasty general protection faults

Share this post


Link to post
Share on other sites
Glak    315
this is easy:

MyClass my_object;

//......

my_object=MyClass(); //everything reset to 0

you see unless you define a constructor for a class it will create a default constructor which calls the default constructor of all of its members, which is essentially what you want to do. For example if you have a member of type float it will call the constructor for that member setting it to 0.0

Share this post


Link to post
Share on other sites
jpetrie    13138
Quote:

this is easy:

MyClass my_object;

//......

my_object=MyClass(); //everything reset to 0

you see unless you define a constructor for a class it will create a default constructor which calls the default constructor of all of its members, which is essentially what you want to do. For example if you have a member of type float it will call the constructor for that member setting it to 0.0


No, it won't. Fundamental types, such as integers and floats, do not have constructors; such types are left uninitialized by an implicit default constructor, and will thus have unpredictable values; what you describe will not work.

Share this post


Link to post
Share on other sites
suliman    1653
yeah if running in debug mode (at least on vc++) the compiler sets most basic variables to zero. But NOT in the resleased build, so that doesnt work...

I guess i just have to do it manually... Ill survive.
Erik

Share this post


Link to post
Share on other sites
Numsgil    501
Quote:
Original post by jpetrie
This still smacks of terrible design, and for more than the obvious "too many members" reason -- you've also got the "internal" design of the class flawed because there are "umpteeen" different places to set variables. Fixing the design would still be the optimal solution here.


No doubt that the design could be improved. But let's suppose that this is a large, preexisting code base. The current design could be very entrenched. Now suppose that your client is changing their minds (alot) about how they want things set up and operating, and even basic features.

"Proper" design is not always the "best" solution. If you just need to get a quick working version of a new idea a client has, sometimes you need to take dirty shortcuts. You can fix them later, that's what refactoring is all about.

It's not a permanent solution, but a bird in the hand is worth two in the bush, as they say. Perfect programming isn't always possible with imperfect knowledge of what your final product should look like.

Quote:

It will cause phantom bugs when you add something to the class that requires specific values to be located in a specific region of memory within the region that is determined by sizeof(classtype) -- the most common example of such is the virtual table pointer, which many implementations will place in the first sizeof(pointer) bytes of an instance.


If it overwrites something important, like a function pointer, your program will let you know the very first time it tries to access that function, with a NULL pointer exception and crash. What I'm saying is that the behavior is very deterministic. It'll run the same way everytime, so as soon as you get a NULL pointer error, and you track it in the debugger to a function call, you know what's up.

But again, it's unlikely to get that far. Usually it'll bomb on the first function call, usually the constructor, if it's not going to work. It just depends on how your compiler aligns class data.

Quote:

I am strongly opposed to doing anything that you know to be a bad idea, no matter how much it seems like a good idea (or that you won't forget to fix it later) at the time; doing stupid things intentionally is just that -- stupid.


I agree when you have infinite time, attention, and resources at your disposal. But I still think that poor practices have a place if they're very localised, and obviously temporary. It speeds up the development process. Once you get a subsystem developmentally stable, that is, unlikely to change greatly in the next few weeks/months, then you can refactor it, removing all the gotchas.

Quote:

You cannot reliably store pointers as members of the class (while a constant integral value of zero will be converted to the null pointer value, the value represention of a null pointer is not necessarily zero, and by using memset() you modify the value representation directly).


This is a pathological case. If the system you're developing for doesn't use 0x0000 to represent a NULL pointer, you'll know that, and so won't use the memset. The number of systems that use something other than 0 is just too small to be taken as part of the general case. I'd wager you could count the number of sytstems on your fingers.

Quote:

You cannot reliably store floating-pointer members (the value representation of floats does not need to be one where 0.0 happens to correspond with an all-zero bitpattern).


Again, this is pathological. You'll know if your floating point representation doesn't use all 0 bits to represent 0, because it's so uncommon. Unless you're interested in extreme cross platform code, this isn't an issue.

Quote:

You cannot store any class type that cannot be reliably "zeroed out" via this same ugly technique.


I would wager that if your compiler lets you do this to one class instance without crashes, it would let you do it to all.

This isn't a for everyone technique. You need to know your strengths and weaknesses as a programmer. If you don't think you'll remember to replace it (a simple search for memset should find all instances), then don't use it. It's also not a long term crutch. You'll want to replace it sooner rather than later, because it's so dependant on how your compiler works.

But never is too strong a proscription, IMO.

Share this post


Link to post
Share on other sites
superpig    1825
Quote:
Original post by jpetrie
Quote:

this is easy:

MyClass my_object;

//......

my_object=MyClass(); //everything reset to 0

you see unless you define a constructor for a class it will create a default constructor which calls the default constructor of all of its members, which is essentially what you want to do. For example if you have a member of type float it will call the constructor for that member setting it to 0.0


No, it won't. Fundamental types, such as integers and floats, do not have constructors; such types are left uninitialized by an implicit default constructor, and will thus have unpredictable values; what you describe will not work.


Indeed. By extension, one way to solve this problem would be to create a new type:


class AutoZeroInt
{
int value;
public:
AutoZeroInt() { value = 0; }
AutoZeroInt(int initial) { value = initial; }
inline operator int&() { return value; }
};



and then use AutoZeroInt for your member variables instead of just int.

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by superpig
Indeed. By extension, one way to solve this problem would be to create a new type:

*** Source Snippet Removed ***

and then use AutoZeroInt for your member variables instead of just int.


I was going to say the same, but using a template :) (Don't you need to overload all the operators, too, though? :s )

Anyway. Nevermind what's being said about the dangers of memset() and so on. The problem is philosophical: for a given class, "set all members to zero" doesn't necessarily mean "put the object in a 'default' state", or even anything meaningful at all. So free your mind of that kind of thought. The "default" state of an object, if that concept is meaningful, is the one that results from instantiating it with the zero-arg constructor, if there is one. If there isn't, it's because (or should be because) the concept *isn't* meaningful for that class.

Therefore, to "zero out" an object, just assign a default-constructed instance to it. (This may give you an opportunity to debug your assignment operator - or for that matter, find out you need one).

As a really simple example: consider a "proper" use of char*, as a "symbol" value - we have a data member of type const char* that will only ever point at one of a specific set of values in a table - we effectively use the pointer value like an enumeration value, but we can also treat it as a pointer value for debugging purposes (i.e. by feeding it to std::cerr). Now, even if it makes sense for one of the "symbols" to be an empty string, we had darned well better not be using a NULL pointer to represent that - we'll want to add an explicit empty string (i.e., "") to our table. (This is actually an example of a very powerful design pattern that often removes lots of special cases from code.)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this