19
struct Foo
{
    Foo(int i)
    {
        ptr = new int(i);
    }
    ~Foo()
    {
        delete ptr;
    }
    int* ptr;
};

int main()
{
    {
        Foo a(8);
        Foo b(7);
        a = b;
    }
    //Do other stuff
}

If I understand correctly, the compiler will automatically create an assignment operator member function for Foo. However, that just takes the value of ptr in b and puts it in a. The memory allocated by a originally seems lost. I could do a call a.~Foo(); before making the assignment, but I heard somewhere that you should rarely need to explicitly call a destructor. So let's say instead I write an assignment operator for Foo that deletes the int pointer of the left operand before assigning the r-value to the l-value. Like so:

Foo& operator=(const Foo& other) 
{
    //To handle self-assignment:
    if (this != &other) {
        delete this->ptr;
        this->ptr = other.ptr;
    }
    return *this;
}

But if I do that, then when Foo a and Foo b go out of scope, don't both their destructors run, deleting the same pointer twice (since they both point to the same thing now)?

Edit:

If I understand Anders K correctly, this is the proper way to do it:

Foo& operator=(const Foo& other) 
{
    //To handle self-assignment:
    if (this != &other) {
        delete this->ptr;
        //Clones the int
        this->ptr = new int(*other.ptr);
    }
    return *this;
}

Now, a cloned the int that b pointed to, and sets its own pointer to it. Perhaps in this situation, the delete and new were not necessary because it just involves ints, but if the data member was not an int* but rather a Bar* or whatnot, a reallocation could be necessary.

Edit 2: The best solution appears to be the copy-and-swap idiom.

Community
  • 1
  • 1
newprogrammer
  • 2,514
  • 2
  • 28
  • 46

3 Answers3

12

Whether this leaks memory?
No it doesn't.

It seems most of the people have missed the point here. So here is a bit of clarification.

The initial response of "No it doesn't leak" in this answer was Incorrect but the solution that was and is suggested here is the only and the most appropriate solution to the problem.


The solution to your woes is:

Not use a pointer to integer member(int *) but to use just an integer (int), You don't really need dynamically allocated pointer member here. You can achieve the same functionality using an int as member.
Note that in C++ You should use new as little as possible.

If for some reason(which I can't see in the code sample) You can't do without dynamically allocated pointer member read on:

You need to follow the Rule of Three!


Why do you need to follow Rule of Three?

The Rule of Three states:

If your class needs either

a copy constructor,
an assignment operator,
or a destructor,

then it is likely to need all three of them.

Your class needs an explicit destructor of its own so it also needs an explicit copy constructor and copy assignment operator.
Since copy constructor and copy assignment operator for your class are implicit, they are implicitly public as well, Which means the class design allows to copy or assign objects of this class. The implicitly generated versions of these functions will only make a shallow copy of the dynamically allocated pointer member, this exposes your class to:

  • Memory Leaks &
  • Dangling pointers &
  • Potential Undefined Behavior of double deallocation

Which basically means you cannot make do with the implicitly generated versions, You need to provide your own overloaded versions and this is what Rule of Three says to begin with.

The explicitly provided overloads should make a deep copy of the allocated member and it thus prevents all your problems.

How to implement the Copy assignment operator correctly?

In this case the most efficient and optimized way of providing a copy assignment operator is by using:
copy-and-swap Idiom
@GManNickG's famous answer provides enough detail to explain the advantages it provides.


Suggestion:

Also, You are much better off using smart pointer as an class member rather than a raw pointer which burdens you with explicit memory management. A smart pointer will implicitly manage the memory for you. What kind of smart pointer to use depends on lifetime and ownership semantics intended for your member and you need to choose an appropriate smart pointer as per your requirement.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 5
    This is wrong. The linked example does indeed leak memory (you can check it with valgrind, as someone else suggested) and dose a double-free on the remaining pointer to boot. – Andy Ross May 26 '12 at 05:08
  • 2
    @AndyRoss: Okay, yes it does but it still doesn't change the solution the answer suggests.The answer was wrong(oversight) in saying *No memory Leak* , but it is more correct than any of the answers can be.I didn't pay much attention to the fact that it leaks memory because the first thing i noticed was the program doesn't follow **Rule of Three** and from there on it pretty much opened a can of worms. – Alok Save May 26 '12 at 05:12
  • 1
    I am use an `int*` rather than an `int` and neglect smart pointers/Rule of Three just to understand these concepts better. I accept this answer mainly because of the linked copy-and-swap idiom. – newprogrammer May 26 '12 at 09:08
  • Any reasoning as to why this still gets downvoted would be illuminating. What do you think is wrong? – Alok Save May 27 '12 at 06:28
  • @newprogrammer: I didn't study at Hogwarts and I am no wizard, neither have a magic wand to guess and read your mind to know what's in there and not expressed in the Q. So either you should be clear in the Q or be ready to hear out logical and correct answers(*which perhaps*) you might know but you didn't tell us that you know.With that said the answers here are encouraged to be complete so that they may help not only the OP but also anyone else who stumbles upon in future,given that it's imperative to write an answer with the necessary details. – Alok Save May 27 '12 at 06:41
10

the normal way to handle this is to create a clone of the object the pointer points to, that is why it is important to have an assignment operator. when there is no assigment operator defined the default behavior is a memcpy which will cause a crash when both destructors try to delete the same object and a memory leak since the previous value ptr was pointing to in b will not be deleted.

Foo a

         +-----+
a->ptr-> |     |
         +-----+

Foo b

         +-----+
b->ptr-> |     |
         +-----+

a = b

         +-----+
         |     |
         +-----+
a->ptr            
       \ +-----+
b->ptr   |     |
         +-----+

when a and b go out of scope delete will be called twice on the same object.

edit: as Benjamin/Als correctly pointed out the above is just referring to this particular example, see below in comments

AndersK
  • 35,813
  • 6
  • 60
  • 86
  • 12
    *"when there is no assigment operator defined the default behavior is a memcpy"* -- No. The default behavior is to recursively call the assignment operator of all sub-objects. In the case of PODs, that's equivalent to a memcpy, yes. Perhaps you weren't speaking in general and only referring to this specific case, but I don't think that's clear from the way you worded it. – Benjamin Lindley May 26 '12 at 05:18
  • 1
    Just to make it clearer, the behavior is defined in C++03,12.8: "*- if the subobject is of class type, the copy assignment operator for the class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes); - if the subobject is an array, each element is assigned, in the manner appropriate to the element type; - if the subobject is of scalar type, the built-in assignment operator is used.*" – Alok Save May 26 '12 at 05:24
  • This answer is not complete.OP also needs to have a copy constructor of his own.He needs to follow the Rule of Three, just providing a overloaded copy assignment operator will work for this example but since the copy constructor is implicitly `public` the class allows copying of its objects,and once that happens OP has the same problem.Copying doesn't happen in this example but it is allowed by the class design( otherwise the copy constructor would have to be marked `private` explicitly). The only correct Solution is to follow the **Rule of Three**. – Alok Save May 26 '12 at 07:18
1

The code as presented has Undefined Behavior. As such, if it leaks memory (as expected) then that is just one possible manifestation of the UB. It can also send an angry threatening letter to Barack Obama, or spew out red (or orange) nasal daemons, or do nothing, or act as if there was no memory leak, miraculously reclaiming the memory, or whatever.

Solution: instead of int*, use int, i.e.

struct Foo
{
    Foo(int i): blah( i ) {}
    int blah;
};

int main()
{
    {
        Foo a(8);
        Foo b(7);
        a = b;
    }
    //Do other stuff
}

That’s safer, shorter, far more efficient and far more clear.

No other solution presented for this question, beats the above on any objective measure.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • +1 double-free sending angry letters. -1 for missing the point of OP's question, though, which is talking about a resource which (OP believes) requires some manual cleanup. If you introduced the concept of RAII explicitly, and why we'd want to achieve it, then you'd definitely be on the money. – Asherah May 26 '12 at 13:44
  • +1 to compensate the silly downvote.OP should be clear about what they are trying to do in the OP, rather than expressing their thought *after* a answer has been posted.This ain't no Hogwarts and there are no wizards with wands to guess and read minds. – Alok Save May 27 '12 at 06:33