Question

I have some difficulties with understanding what is really done behind returning values in C++.

Let's have following code:

class MyClass {

public:

    int id;

    MyClass(int id) {
        this->id = id;
        cout << "[" << id << "] MyClass::ctor\n";
    }

    MyClass(const MyClass& other) {
        cout << "[" << id << "] MyClass::ctor&\n";
    }

    ~MyClass() {
        cout << "[" << id << "] MyClass::dtor\n";
    }

    MyClass& operator=(const MyClass& r) {
        cout << "[" << id << "] MyClass::operator=\n";
        return *this;
    }

};

MyClass foo() {
    MyClass c(111);  
    return c;        
}

MyClass& bar() {
    MyClass c(222);
    return c;
}

MyClass* baz() {
    MyClass* c = new MyClass(333);
    return c;
}

I use gcc 4.7.3.

Case 1

When I call:

MyClass c1 = foo();
cout << c1.id << endl;

The output is:

[111] MyClass::ctor
111
[111] MyClass::dtor

My understanding is that in foo object is created on the stack and then destroyed upon return statement because it's end of a scope. Returning is done by object copying (copy constructor) which is later assigned to c1 in main (assignment operator). If I'm right why there is no output from copy constructor nor assignment operator? Is this because of RVO?

Case 2

When I call:

MyClass c2 = bar();
cout << c2.id << endl;

The output is:

[222] MyClass::ctor
[222] MyClass::dtor
[4197488] MyClass::ctor&
4197488
[4197488] MyClass::dtor

What is going on here? I create variable then return it and variable is destroyed because it is end of a scope. Compiler is trying copy that variable by copy constructor but It is already destroyed and that's why I have random value? So what is actually in c2 in main?

Case 3

When I call:

MyClass* c3 = baz();
cout << c3->id << endl;

The output is:

[333] MyClass::ctor
333

This is the simplest case? I return a dynamically created pointer which lies on heap, so memmory is allocated and not automatically freed. This is the case when destructor isn't called and I have memory leak. Am I right?

Are there any other cases or things that aren't obvious and I should know to fully master returning values in C++? ;) What is a recommended way to return a object from function (if any) - any rules of thumb upon that?

Was it helpful?

Solution 2

Case 1:

MyClass foo() {
    MyClass c(111);  
    return c;        
}
...
MyClass c1 = foo();

is a typical case when RVO can be applied. This is called copy-initialization and the assignment operator is not used since the object is created in place, unlike the situation:

MyClass c1;
c1 = foo();

where c1 is constructed, temporary c in foo() is constructed, [ copy of c is constructed ], c or copy of c is assigned to c1, [ copy of c is destructed] and c is destructed. (what exactly happens depends on whether the compiler eliminates the redundant copy of c being created or not).

Case 2:

MyClass& bar() {
    MyClass c(222);
    return c;
}
...
MyClass c2 = bar();

invokes undefined behavior since you are returning a reference to local (temporary) variable c ~ an object with automatic storage duration.

Case 3:

MyClass* baz() {
    MyClass* c = new MyClass(333);
    return c;
}
...
MyClass c2 = bar();

is the most straightforward one since you control what happens yet with a very unpleasant consequence: you are responsible for memory management, which is the reason why you should avoid dynamic allocation of this kind always when it is possible (and prefer Case 1).

OTHER TIPS

May I just add that case #2 is one of the cases of undefined behavior in the C++ language, since returning a reference to a local variable is illegal. This is because a local variable has a precisely defined lifetime, and - by returning it by a reference - you're returning a reference to a variable that does not exist anymore when the function returns. Therefore, you exhibit undefined behavior and the value of the given variable is practically random. As is the result of the rest of your program, since Anything at all can happen.

Most compilers will issue a warning when you try to do something like this (either return a local variable by reference, or by address) - gcc, for example, tells me something like this :

bla.cpp:37:13: warning: reference to local variable ‘c’ returned [-Wreturn-local-addr]

You should remember, however, that the compiler is not at all required to issue any kind of warning when a statement that may exhibit undefined behavior occurs. Situations such as this one, though, must be avoided at all costs, because they're practically never right.

1) Yes.
2) You have a random value because your copy c'tor and operator= don't copy the value of id. However, you are correct in assuming there is no relying on the value of an object after it has been deleted.
3) Yes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top