11
#include <iostream>

struct person_t{
        int age;
};

person_t get_person1(){
        person_t person;
        person.age = 10;
        return person;
}

person_t * get_person2(){
        person_t *person = new person_t;
        person->age = 20;
        return person;
}

int main(){
        person_t person1 = get_person1();
        person_t *person2 = get_person2();
        std::cout << person1.age << std::endl;
        std::cout << person2->age << std::endl;
        delete person2;
        return 0;
}

I want to know what is the safest way to return a structure from a function.

As in the answers to the questions in here and here, it is said that when you create a object as in get_person1(), that object will be destroyed after when it goes out of scope.

But when I search for "How to return a struct from function c++", it suggest me method one (with get_person1()) (Example here). But I think that method will destroy the object after the function was called and I think method 2 is the safest.

Am I wrong here..? Or any opinion regarding this topic..?

Thank you!!

Ramesh-X
  • 4,853
  • 6
  • 46
  • 67
  • 2
    It returns a copy and then destroys the original. – Barmar Feb 10 '18 at 10:00
  • 1
    So it's perfectly safe. – Barmar Feb 10 '18 at 10:01
  • 2
    In this code example it is all perfectly safe, person1 lives until main goes out of scope, person 1 is returned to you by either copy (or most likely RVO) or maybe even constructed in place depending on what the compiler feels like, either way you have an object in main that lives as long as your application does. – arynaq Feb 10 '18 at 10:05

4 Answers4

8

Use return by value for 3 reasons:

  • It makes your code readable
  • Your struct here is small (one int) so it is like returning an int. You can do that in C++, it is efficient
  • In C++17 (and most compiler before that) the cost of the intermediate copy of the object will be avoided. It is known as RVO. You need to put only one return in your get_person function.
Gabriel
  • 3,564
  • 1
  • 27
  • 49
  • 2
    Well, RVO worked long before C++17, which just made it mandatory. – lisyarus Feb 10 '18 at 10:07
  • 2
    exactly. As I sad, in most compilers before that – Gabriel Feb 10 '18 at 10:08
  • 1
    Ah, indeed, missed that phrase! My apologies. – lisyarus Feb 10 '18 at 10:19
  • 1
    The expression `get_person2()` will be equivalent to `new person_t(get_person1())`. But, if one wants to make get_person2 safer by making it return a `unique_ptr`, the expression `std::make_unique(get_person1())` will not be equivalent to `get_person2()`. Apparently this is considered as a core language issue, see this [answer](https://stackoverflow.com/a/48696012/5632316). So both function `get_person1` and `get_person2` shall be considered for efficiency [assembly here](https://godbolt.org/g/F5MPLM). – Oliv Feb 10 '18 at 11:07
4

it is said that when you create a object as in get_person1(), that object will be destroyed after when it goes out of scope.

What is destroyed is the local object (i.e.: person inside get_persion1()). What is returned is a copy of that object: a struct person_t is copied (it may be moved as well). So, it is safe.


get_person2() is also safe, but consider using smart pointers instead of raw pointers :

std::unique_ptr<person_t> get_person2(){
        auto person = std::make_unique<person_t>();
        // For pre-C++14
        // std::unique_ptr<person_t> person(new person_t);
        person->age = 20;
        return person;
}

That way, the caller to get_person2() doesn't have to call delete (it may forget to do so).

JFMR
  • 23,265
  • 4
  • 52
  • 76
4

Both approaches are equally safe - neither one causes undefined behavior, and the value set inside the function makes it back to the caller.

The main difference is that the first approach copies the struct, while the second approach copies a pointer to struct. When the struct is tiny, such as in your example, there is no difference. When the struct becomes large, returning a pointer may save you some CPU cycles at the expense of additional memory allocation, so it is far from being a guaranteed win.

Obviously, the second approach has another drawback in that one has to delete the struct eventually. The first approach is free from this requirement.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
2

Both methods are safe.

Method 1 is safe since the local person is copied out from the function scope. Oftentimes, Return Value Optimization (RVO) can be used to avoid copying.

Method 2 is also safe since memory for an instance of person is allocated in the heap. This allocation is obviously valid outside the scope of the function. However, you must remember to deallocate said memory when you are done using it. This is not critical in your example code. Heap-allocations are automatically deallocated when main terminates, so for your short example you do not actually "need" delete person2.

Your question is which of said methods are "safest". This is not possible to answer objectively, although I would argue that a wide majority of C++ programmers would advise against method 2 in favor of method 1. Many C++ programmers would recommend using smart-pointers.

Final remark: Method 1 and method 2 are fundamentally different. Allocating memory on the stack (method 1) versus allocating memory on the heap (method 2) are two different concepts. There are many more considerations to make but "safety" considerations.

Joakim Thorén
  • 1,111
  • 10
  • 17