Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does this C++ code leak memory?

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.

like image 210
newprogrammer Avatar asked May 26 '12 04:05

newprogrammer


2 Answers

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.

like image 192
Alok Save Avatar answered Sep 23 '22 12:09

Alok Save


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

like image 28
AndersK Avatar answered Sep 20 '22 12:09

AndersK