Damage due to passing by value in C++

186 views Asked by At

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?

2

There are 2 answers

2
Some programmer dude On BEST ANSWER

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) { ... }
0
463035818_is_not_an_ai On

What they refer to might be something like this:

struct bar { };

void foo(bar x, bar* y) {
       delete y;
}

int main() {
    bar* a = new bar;
    bar& b = a;
    foo(b, a);
}

foo gets passed b by value, it cannot modify the object through that copy directly. Though the same function can delete the same object through the other parameter. How or why this is relevant, I cannot tell though. And I cannot make sense of the last sentence

"This could happen when the destructor of the object is called within the function to delete the local copy of the object"

In above example this would mean that x going out of scope would somehow affect the original bar object. Though x is just a copy and I cannot imagine how this could happen unless bar is broken in the first place.