0

Suppose I have:

    class SomeObject {

    };

    SomeObject& f() {
        SomeObject *s = new SomeObject();
        return *s;
    }

    // Variant 1
    int main() {
        SomeObject& s = f();
        // Do something with s
    }

   // Variant 2
    int main() {
        SomeObject s = f();
        // Do something with s
    }

Is there any difference between the first variant and the second? any cases I would use one over the other?

Edit: One more question, what does s contain in both cases?

Shmoopy
  • 5,334
  • 4
  • 36
  • 72
  • 2
    In the first case, you atleast have a chance to cleanup, in the second case, you're leaking memory... – Nim Nov 14 '13 at 10:02
  • With the way `f` is defined, the second is a memory leak. – StoryTeller - Unslander Monica Nov 14 '13 at 10:02
  • 2
    Just plain wrong. It's very counter-intuitive having to receive something by reference where the intent is for me to own the value. More distressing is the need to do something like `delete &s;` afterwards... – Mark Garcia Nov 14 '13 at 10:04
  • @Nim I'm more interested with what is s in both cases? meaning, what happens when you return by reference to a variable not declared as a reference – Shmoopy Nov 14 '13 at 10:06
  • @Shmoopy, define a copy c'tor that prints and see for yourself. – StoryTeller - Unslander Monica Nov 14 '13 at 10:07
  • Alternatives to returning a reference to a heap allocated object: construct on the stack and "move" the object with std::move; return a std::unique_ptr to the heap allocated object. – A.B. Nov 14 '13 at 10:07
  • 2
    @MarkGarcia Yes. The almost universal convention is that return by reference doesn't transfer any ownership, but is used to allow modification of an object owned by the "function" (which is almost always a member function, or somehow associated with data for which it is responsible). – James Kanze Nov 14 '13 at 10:17
  • 1
    @A.B, it should be constructed on the stack, but it should not be `move`d. Return values in C++11 are implicitly `move`d on your behalf. See [http://stackoverflow.com/questions/17473753/c11-return-value-optimization-or-move](this question on the matter). – Aaron McDaid Nov 14 '13 at 10:34
  • @Shmoopy, you should probably return by value `SomeObject`. And no, it won't be as slow as you might think it is! It may be faster than returning a pointer, as `new` can be quite slow sometimes. Or a pointer `SomeObject*`, to make clear that the caller has to take ownership of the object. Or better still, use `unique_ptr` or `shared_ptr`. (Are you allowed to use C++11 ? ). Return by reference only in the situation @JamesKanze is talking about. – Aaron McDaid Nov 14 '13 at 10:37

4 Answers4

2

First, you never want to return a reference to an object which was dynamically allocated in the function. This is a memory leak waiting to happen.

Beyond that, it depends on the semantics of the object, and what you are doing. Using the reference (variant 1) allows modification of the object it refers to, so that some other function will see the modified value. Declaring a value (variant 2) means that you have your own local copy, and any modifications, etc. will be to it, and not to the object referred to in the function return.

Typically, if a function returns a reference to a non-const, it's because it expects the value to be modified; a typical example would be something like std::vector<>::operator[], where an expression like:

v[i] = 42;

is expected to modify the element in the vector. If this is not the case, then the function should return a value, not a reference (and you should almost never use such a function to initialize a local reference). And of course, this only makes sense if you return a reference to something that is accessible elsewhere; either a global variable or (far more likely) data owned by the class of which the function is a member.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

In the first variant you attach a reference directly to a dynamically allocated object. This is a rather unorthodox way to own dynamic memory (a pointer would be better suited for that purpose), but still it gives you the opportunity to properly deallocate that object. I.e. at the end of your first main you can do

delete &s;

In the second variant you lose the reference, i.e. you lose the only link to that dynamically allocated object. The object becomes a memory leak.

Again, owning a dynamically allocated object through a reference does not strike me as a good practice. It is usually better to use a pointer or a smart pointer for that purpose. For that reason, both of your variants are flawed, even though the first one is formally redeemable.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
0

Variant 1 will copy the address of the object and will be fast

Variant 2 will copy the whole object and will be slow (as already pointed out in Variant2 you cant delete the object which you created by calling new)

for the edit: Both f contain the same Object

  • 1
    No, one is a copy of an object, the other is a reference to an object. Those don't "contain the same object". – StoryTeller - Unslander Monica Nov 14 '13 at 10:06
  • 1
    First, of course: the semantics of the two variants are _not_ the same: in the first case, modifications to the object through the initialized variable will modify the original object; in the second, only the local copy. And second, even in the second case, the copy may or may not take place. Most of the time, most compilers will optimize it out. – James Kanze Nov 14 '13 at 10:13
0

None of the two options you asked about is very good. In this particular case you should use shared_ptr or unique_ptr, or auto_ptr if you use older C++ compilers, and change the function so it returns pointer, not reference. Another good option is returning the object by value, especially if the object is small and cheap to construct.

Modification to return the object by value:

SomeObject f() { return SomeObject(); }

SomeObject s(f());

Simple, clean, safe - no memory leaking here.

Using unique_ptr:

SomeObject* f() { return new SomeObject(); }

unique_ptr<SomeObject> s(f());

One of the advantages of using a unique_ptr or shared_ptr here is that you can change your function f at some point to return objects of a class derived from SomeObject and none of your client code will need to be changed - just make sure the base class (SomeObject) has a virtual constructor.

Why the options you were considering are not very good:

Variant 1:

SomeObject& s = f();

How are you going to destroy the object? You will need address of the object to call it's destructor anyway, so at some point you would need to dereference the object that s refers to (&s)

Variant 2. You have a leak here and not a chance to call destructor of the object returned from your function.

piokuc
  • 25,594
  • 11
  • 72
  • 102
  • Are you sure that a smart pointer is indicated here? To me, it looks more like the function shouldn't return a reference, but a value, and that no dynamic allocation should be involved. – James Kanze Nov 14 '13 at 10:15
  • Well, whether the function should return a value or a pointer depends on the class of the object returned: if this is something small, simple and cheap to copy than by value is preferred. But sometimes you cannot afford to copy objects unnecessarily . – piokuc Nov 14 '13 at 10:19
  • @JamesKanze But it's a good point, I will add the option to my answer. – piokuc Nov 14 '13 at 10:20
  • Whether a function should return a value or a pointer depends on the semantics involved. If the profiler shows that the copying is a problem, then you might have to revise, but generally, compilers are very good at optimizing out unnecessary copies (and if you have C++11, adding move semantics to the object may allow you to keep the appearance of value semantics anyway---again, not something to do prematurely, but if the optimizer says you must, it's a very nice option to have available). – James Kanze Nov 14 '13 at 10:34
  • Ok, I agree. But there is another reason why returning a pointer may be be advantageous: polymorphism. I have updated my answer. – piokuc Nov 14 '13 at 10:37