In the book "C++ IT-Tutorial" by Herbert Schildt, chapter 9 page 368, the following problem is pointed out:
Even if you pass an object to a function by value, in which case the passed object should be theoretically isolated and protected, the object can still be changed or even destroyed by side effects. This could happen when the destructor of the object is called within the function to delete the local copy of the object.
The paragraph was written to motivate the use of copy constructors.
Unfortunately, I could not find a minimal example to reproduce this behavior, although most of the other problems in the book are documented with simple examples. Could you suggest one?
This usually happens because of the programmer not following the rules of three, five or zero.
When you pass an object by value, the compiler will create code to make a shallow copy. Take for example an object which have a pointer member variable. Copying the object will copy the pointer and not the data that it's pointing to. When the copy is destructed it delete
the memory, which means the pointer in the original object no longer is valid, since it still points to the now deleted memory.
For a quick example:
struct S
{
// Some data
};
struct Bad
{
S* pointer;
Bad()
: pointer{ new S } // Create an object, and initialize the pointer
{}
~Bad()
{
delete pointer;
}
};
void f(Bad copy)
{
// Do something...
}
int main()
{
Bad original;
f(original);
}
When calling the function f
, a copy of the object original
is made. This copy will have the life-time of the run-time of f
. When the life-time of copy
ends its destructor will be called. But since both original
and copy
are pointing to the very same S
object, the pointer in original
will become invalid.
This problem can be solved in a couple of ways. The first and most naive is to create a Bad
copy-constructor which creates its own copy of S
, so it's independent of the original.
A somewhat better solution is to use something like std::shared_ptr<S>
instead. That is usually used for marking ownership but could work here.
And even better solution is to not have the S
object as a pointer at all, because then the compiler-generated copy-constructor will make sure to create a copy of the S
object as well. Though it might not be possible in all cases (think polymorphism).
But what I think is the best solution is to pass the object by reference instead:
void f(Bad const& reference) { ... }