1

I am currently reading the second edition of C++: A Beginner's Guide by Herbert Schildt.

In Module 9.4, he talks about returning objects:

Just as objects can be passed to functions, functions can return objects. To return an object, first declare the function as returning a class type. Second, return an object of that type using the normal return statement. The following program has a member function called mkBigger( ). It returns an object that gives val a value twice as large as the invoking object.

This is the 'following program' he mentions:

// Returning objects.
#include <iostream>
using namespace std;

class MyClass {
int val;
public:
    // Normal Constructor.
    MyClass(int i) {
        val = i;
        cout << "Inside constructor\n";
    }

    ~MyClass() {
    cout << "Destructing\n";
    }

    int getval() { return val; }

    // Return an object.
    MyClass mkBigger() {
        Myclass o(val * 2); // mkBigger() returns a MyClass object.

        return o;
    }
};

void display(MyClass ob)
{
    cout << ob.getval() << '\n';
}

int main()
{
    cout << " Before Constructing a.\n";
    MyClass a;
    cout << "After constructing a.\n\n";

    cout << "Before call to display.\n";
    display(a);
    cout << "After display() returns.\n\n";

    cout << "Before call to mkBigger().\n";
    a = a.mkBigger();
    cout << "After mkBigger() returns.\n\n";

    cout << "Before second call to display.\n";
    display(a);
    cout << "After display() returns.\n\n";

    return 0;
}

This gives us the following output:

Before Constructing a.
Inside constructor
After constructing a.

Before call to display.
10
Destructing
After display() returns.

Before call to mkBigger()
Inside constructor
Destructing
Destructing
After mkBigger() returns.

Before second call to display.
20
Destructing
After display() returns.

Destructing

Schildt then goes on to explain that the reason there are two 'Destructing' messages during the mkBigger() call is because of the fact that:

when an object is returned by a function, a temporary object is automatically created, which holds the return value. It is this object that is actually returned by the function. After the value has been returned, this object is destroyed.

I was actually surprised there wasn't 3 'Destructing' messages. I have the following issue: Given the definition of mkBigger(), a new MyClass instance is created, and it is that instance that is returned and placed in the address of a. Thus, when doing

a = a.mkBigger();

My impression is thus that the original object previously held in a is no longer referenced by a. Is this correct? If so, I then have the following issues:

I was told C++ has some minute notions of garbage collection. Would that object thus be garbage-collected? where is this object now? Is this an example of the possible feared memory leaks that many mention when talking about the 'dangers' of C++?

Arnav Borborah
  • 11,357
  • 8
  • 43
  • 88
Devilius
  • 311
  • 1
  • 3
  • 12
  • 5
    I strongly recommend you learn C++ from a better source than Schildt's works. Also, if you are going to instrument destructors, you should also instrument the copy constructor and assignment operator. –  Aug 04 '17 at 15:27
  • 1
    Such as one of [these](https://stackoverflow.com/a/388282/6525260) maybe? – Arnav Borborah Aug 04 '17 at 15:29
  • I actually just came across a lot of people criticising his book whilst trying to find an answer for my question. I'll look for a better book. – Devilius Aug 04 '17 at 15:30
  • 1
    Garbage collection? Not at all... Your old object was destroyed and it's no longer accessible. No memory leaks. – Jacek Aug 04 '17 at 15:31
  • If I were you I would find a better book. The guy should have at least decorated the copy constructor so you can see these copies being made. Also C++ is not garbage collected per se. What it does have to automatic storage duration which and objects with that are automatically destroyed once they go out of scope. – NathanOliver Aug 04 '17 at 15:32
  • If so, why is there not a third 'Destructing' message? Schild seems to imply that the first is the destruction of the function-call's local object, and the second is from the temporary object created when an object is return by a function. – Devilius Aug 04 '17 at 15:32
  • 3
    @Devilius For that you need to read [this](https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) – NathanOliver Aug 04 '17 at 15:33

1 Answers1

3

One of the destructor in mkbigger() is called on o, the MyClass instance passed in by value; it goes out of scope at the end of the function. The other is called on the temporary copy of o returned when it is destroyed. What else goes out of scope? Not a in main(); therefore you should not expect a third destructor to be called. C++ does not provide garbage collection outside of calling destructors when automatic objects go out of scope.

Unlike some other modern languages, a does not "hold a reference" to an object; a is the object, in that it is a certain number of bytes holding the raw data members. When you do a = a.mkBigger();, MyClass's default assignment operator is called, which simply copies the val inside the temporary object on the right hand side into the val inside a, overwriting the value that was already there. a = a.makeBigger() would be equivalent to a.val = a.makeBigger().val if val were public.

Memory leaks occur when you use new to allocate memory and then fail to use delete to deallocate that memory. For classes that do this internally, you must write at least your own copy constructor, assignment operator, and destructor.

joshwilsonvu
  • 2,569
  • 9
  • 20