Operator overload, why return reference?

I was working on some simple examples from a c++ book and came across this sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

using namespace std;

class Counter
{
    public:
    Counter();
    ~Counter(){}
    int GetVal() const { return val; }
    void SetVal(int x) { val = x; }
    const Counter& operator++ ();
    
    private:
    int val;
};

Counter::Counter(): val(0) { cout << "calling..." << endl;};

const Counter& Counter::operator++()
{
    ++val;
    return *this;
}

int main()
{
    Counter i;
    ++i;
    cout << "i: " << i.GetVal() << endl;
    Counter a = ++i;
    cout << "&i: " << &i << endl;
    cout << "i : " << i.GetVal() << endl;
    
    return 0;
}


My question is why is the return type of the operator++ a reference? I know it's to avoid creating a new object, but if I change it to:

const Counter Counter::operator++()

it doesn't call the constructor. Is it still creating a Counter object to return? How is the memory managed?
it doesn't call the constructor.


It does, it's just not calling the default constructor. it's calling the copy constructor. Since you did not provide a copy constructor it's just using the compiler-provided version.

Try this:

1
2
3
4
5
Counter::Counter( const Counter& toCopy )
  : val( toCopy.val )
{
  cout << "copying...";
}
Ahh.. ok I see. Then when does the newly created object go out of scope?
At the end of the expression...
19
20
21
22
Counter::~Counter()
{
    cout << "Destructing..." << endl;
}
34
35
36
37
38
39
Counter t;
cout << "before" << endl;
++t;
cout << "after" << endl;
calling... (t)
before
copying... (the temporary)
Destructing... (the temporary)
after
Destructing... (t)
Last edited on
I still don't quite fully understand. LB, what you wrote makes sense to me but when I run this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>

using namespace std;

class Counter
{
    public:
    Counter();
    Counter(const Counter &); // Copy constructor
    ~Counter(){ cout << "destructor..." << endl; }
    int GetVal() const { return val; }
    void SetVal(int x) { val = x; }
    const Counter operator++ ();
    
    private:
    int val;
};

Counter::Counter() : val(0) { cout << "calling..." << endl; };

Counter::Counter(const Counter& rhs) : val(rhs.GetVal()) { cout << "copy constructor..." << endl; };

const Counter Counter::operator++()
{
    ++val;
    return *this;
}

int main()
{
    Counter i;
    cout << "i: " << i.GetVal() << endl;
    Counter a = ++i;
    cout << "i : " << i.GetVal() << endl;
    
    return 0;
}


I get the following output:

calling...
i: 0
copy constructor...
i : 1
destructor...
destructor...


And I get the same output by changing the operator to a return by reference. Where's that temporary object? Why isn't there an extra call to the copy/destructor? I'm sorry if this seems obvious... and thank you so much for your help!!
Try disabling optimization (that's a first!)
There doesn't seem to be a temporary in your example, as it's being copied directly into 'a'.

Try this:

1
2
3
4
    Counter i;
    cout << "i: " << i.GetVal() << endl;  
    ++i;  // <- don't assign to anything, temporary [possibly] constructed and destructed here
    cout << "i : " << i.GetVal() << endl;


Note that I threw the "possibly" in there because it's possible the compiler will optimize away the temporary object altogether.

My question is why is the return type of the operator++ a reference? I know it's to avoid creating a new object, but if I change it to:


It 'could' also be used to chain invocations, albeit it probably doesn't make much sense in this example.

1
2
3
4
5
6
7
//Counter can also be used like this..

Counter i;

cout << "i: " << (++i).GetVal() << endl;

//.... 


Yes it doesn't make much sense, but chaining can be useful in some circumstances.
I do this when I'm feeling lazy:
++ ++i
But other than that and what clanmjc mentioned, it's just for a performance gain in the case of a compiler that doesn't optimize it. And just as with all overloadable operators, it can do and return anything it wants, but it's recommended to be used for its intended purpose.
Oh ok. I see. But for ++ ++i to work it would have to return a non-const (I think).

With optimization off I do see the extra call with: ++i

Thanks for helping me understand!
Topic archived. No new replies allowed.