Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given are the Various Ways to return an object in C++, what are the potential Issues with each of these return statements

Below is the code which has various return statements and all are working perfectly fine. Compiler throws the warning for fun_ret_obj1

Test.cpp: In function ‘myClass& fun_ret_obj1()’: Test.cpp:45: warning: reference to local variable ‘myObj’ returned

But still the output seems to be fine.Is it by Chance? Are there any catches with any of the return statements below?
Explanation would be really helpful, Thanks

#include <iostream>
 using namespace std;


 class myClass {
 public:
 int a ;
 myClass()
 {
   a = 10;
 }
 };
 myClass& fun_ret_add()
 {
    myClass *ptr = new myClass();
    return *ptr;
 }

 myClass* fun_ret_ptr()
 {
     myClass *ptr = new myClass();
     return ptr ;
 }

 myClass fun_ret_obj()
 {
     myClass myObj;
     return myObj;
 }

 myClass& fun_ret_obj1()
 {
     myClass myObj;
     return myObj;
 }


 int main()
 {
     myClass obj,obj1;
     std::cout <<"In Main \n";

     myClass *a = fun_ret_ptr();
     std::cout<<a->a<<"\n";

     myClass &b = fun_ret_add();
     std::cout<<b.a<<"\n";

     myClass c = fun_ret_obj();
     std::cout<<c.a<<"\n";

     myClass d = fun_ret_obj1();
     std::cout<<d.a<<"\n";

 }
like image 500
LearningCpp Avatar asked Dec 02 '22 12:12

LearningCpp


1 Answers

First one is a memory leak:

myClass& fun_ret_add()
 {
    myClass *ptr = new myClass();
    return *ptr;
 }

Second one returns a raw pointer (evil - return a std::unique_ptr)

 myClass* fun_ret_ptr()
 {
     myClass *ptr = new myClass();
     return ptr ;
 }

Third one is perfect - returns a copy, which will almost always be elided. In c++17 it's guaranteed to be elided. This is efficient and safe.

 myClass fun_ret_obj()
 {
     myClass myObj;
     return myObj;
 }

update

In c++17 you could guarantee the elision of the copy this way:

 myClass fun_ret_obj()
 {
     return myClass{};
 }

end of update

Fourth one is undefined behaviour. Returning a reference to a non-existent object. Never do this.

 myClass& fun_ret_obj1()
 {
     myClass myObj;
     return myObj;
 }

regarding memory leaks

It is true that in the first example, a caller could release the memory if he/she knew that myClass had been allocated with new:

auto& x = fun_ret_add();    // a
...
delete std::addressof(x);   // b

This would require:

  1. That the caller knows that fun_ret_add() is implemented in terms of new.
  2. That the implementation of fun_ret_add() never changes
  3. That no exceptions occur between (a) and (b)

The second example is similar. In this case, at least there is a hint that the object needs to be deleted, but the caller must still know that the object has been allocated with new, and he must guard against exceptions.

Contrast with this:

std::unique_ptr<myClass> fun_ret_ptr()
{
    return std::make_unique<myClass>();
    // or
    return { new myClass() };
    // or
    return std::unique_ptr<myClass>(new myClass());
}

Now the caller receives a smart pointer. If the caller does nothing but use this pointer, the myClass object will be properly deleted when the pointer goes out of scope, and all memory will be reclaimed.

like image 129
Richard Hodges Avatar answered Dec 05 '22 07:12

Richard Hodges