views:

92

answers:

4

I have this:

enum Units { Pounds, Kilos };

struct Configuration
{
    const Units units;
    const char *name;

    inline Configuration(Units pUnits, char *pName) : units(pUnits)
    {
        name = strdup(pName);
    }

    inline ~Configuration() { free((void *)name); }
};

I was passing one of these to a method like this:

Configuration cc(Kilos, "abc");
cdao->write(cc);

I was getting nasty crashes from this until I tried redefining the method to take a reference:

Configuration cc(Kilos, "abc");
cdao->write(&cc);

And now everything works.

But how could the struct by value be screwing with memory?

+6  A: 

The fact that you are using strdup indicates that there is something wrong with your code, and the wrong thing is you don't have a copy constructor. Any time you have a destructor, you almost certainly also need a copy constructor, which will copy the object correctly when you call by value.

To improve your code:

  • create a copy constructor and probably an assignment operator which allocate and copy the string correctly

  • better yet, get rid of strdup - use a std:;string, in which case you won't need a destructor, copy ctor or assignment op.

  • get rid of the "inline" keywords - they are doing nothing.

anon
inline keywords make sense when used in method definitions declared "outside" of the class, right?
Germán
@German Mostly not - the only real reason to use inline is if you #include the function source in more than one translation unit.
anon
+1  A: 

When you call it with out the reference, its copying units and *name but not the values inside of *name. So when that temporary object is destructed, its freeing *name from all instances of Configuration.

Daniel A. White
+1  A: 

you have to add your struct a copy constructor and handle char *name; (mean, allocate and delete memory, initialize with value).

Anyway, it is a not good idea to use char * for strings. Use std::string, that will handle everything for you.

alexanderb
It's probably OK for a student to learn the language from the bottom up. If he had std::string yet, he wouldn't have reason to learn about copy constructors now.
Luther Blissett
And maybe its ok, if he wouldn't (learn it now). Its probably better to see that you can go very far without ever creating one, and then learn them if/when you really need them.
Fabio Fracassi
Yep... I haven't touched C++ for a long time, and I was more into C anyway. I must never have stumbled with this issue before, I guess I always passed objects by ref (and I guess I will stick to that now).And yes, I prefer to understand what's going on and what I should do to make it work with char *. Thanks everyone.
Germán
+1  A: 

You didn't provide your own copy constructor or assignment operator. So, when you make a copy or an assignment, the compiler-generated copy constructors and assignment operators are used which actually don't do the right thing in this case. They simply copy every member so you end up with two Configuration objects referring to the same character array. And both Configuration objects feel responsible for deleting the array which almost certainly leads to a "double-deletion" error.

Keep in mind the "rule of three". The problem here is that the pointer doesn't behave like you want it to. If you had used a std::string as member you wouldn't have to write your own copy constructor, destructor, assignment operator. That's because the compiler-generated ones simply invoke the relevant operations on their members and the string-member already handles this correctly -- unlike a pointer to char.

sellibitze
Great answers everyone, I guess I like this one better. Thanks.
Germán