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 int
s, 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.
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:
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:
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With