6

I know there are similar questions to this but none of them give a definitive answer to my question...

Are both these okay in terms of best practices? Or should I be returning a pointer? And if not how should they be changed to follow best practices.

I want to return a reference to a new object from a function. My implementation is as follow:

MyClass& doSomething() {
   return *(new MyClass());
}

MyClass a = doSomething();

Is this okay because a new instance of MyClass is being allocated on the heap with new?

Or should I be making it constant (I'm not really sure when to do this or not)?

const MyClass& doSomething() {
    return *(new MyClass());
}

And if both these are wrong should I just be returning a pointer to the new object?

Thanks.

Chris Kdon
  • 916
  • 1
  • 14
  • 26
  • No const necessary, first option is good. Also, you can return a pointer as well if you want. Up to you. – Serge Seredenko Oct 16 '13 at 02:52
  • 1
    But the reference wasn't allocated by the caller. It was instantiated during the call..thus I figured if you didn't use new then it would go out of scope because it would be on the stack rather than the heap. – Chris Kdon Oct 16 '13 at 02:53
  • 4
    @BrianCain He absolutely needs `new`, because otherwise the object will get destructed as soon as `doSomething()` finishes. – Serge Seredenko Oct 16 '13 at 02:53
  • @SergeSeredenko, stricken, sorry. – Brian Cain Oct 16 '13 at 03:02

1 Answers1

8

While this isn't exactly invalid, it's not a good idea.

MyClass& doSomething() {
   return *(new MyClass());
}

After you return, nobody has the original pointer, so nobody will ever delete it.* So it's a memory leak.

You should pretty much never write a new unless you have a corresponding delete—or, better, a smart pointer constructor.


Meanwhile, this line in your original code:

MyClass a = doSomething();

… is going to make a copy of the value anyway. Assuming that wasn't another bug that has to be fixed, why bother heap-allocating an object and returning a reference to copy and leak? Just return the object by value:

MyClass doSomething() {
   return MyClass();
}

Now you don't have to worry about deleting anything, because you never created anything on the heap.


Best practices can usually be summed up in the four letters RAII: Resource Acquisition Is Initialization. (And the corollary, that destruction is release.) If you have something that's impossible, or expensive, to pass around by value, then pass around some handle to it by value. For example:

unique_ptr<MyClass> doSomething() {
    return unique_ptr<MyClass>(new myClass());
}

unique_ptr<MyClass> a = doSomething();

Now it's just a pointer being copied around. The object itself gets created inside doSomething, and deleted whenever a goes out of scope (or, if you pass it along to another variable, whenever that goes out of scope, etc.).

On the other hand, if MyClass is just a handful of easily-copyable values**, just copy it.


* It's not impossible to ever delete it; you can always take a pointer to the reference and delete that. It's just very unlikely you will ever do so, and it will look awkward. If you want to pass pointers around, pass pointers around. If you don't want to pass pointers around, wrap ownership up in a class and pass the class around by value.

** By "easily-copyable" I mean that it's easy to copy them safely, and that you actually do so. For example, a raw pointer or a file handle is just a few bytes, and the default copy constructor will gladly copy them over for you… but then you end up with multiple references to the same heap object or file, and it's impossible to track who's in charge of deleting or closing it.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • Is the latter assuming no RVO? Or did I miss something... (I do often, so so surprise if so). – WhozCraig Oct 16 '13 at 03:01
  • @WhozCraig: With or without RVO, the compiler can't optimize out the newly-created pointer, because you explicitly asked for it. If you return by value instead of by reference, _then_ RVO can optimize out a copy, and the whole thing will be essentially free. – abarnert Oct 16 '13 at 03:03
  • I would have to create the class outside of the function to pass it around by value correct? Thanks for the memory leak info I thought something weird was going on but I couldn't quite figure out what it was. Basically what I'm creating is a sort of factory method. So returning a pointer should work then? – Chris Kdon Oct 16 '13 at 03:05
  • I meant your final snippet in relation to the followup sentence, but I see now it was in reference to the OP's code, not a blanket this-will-copy-anyway so just-do-this. I agree, by-value with either RVO or move-construction is totally the way to do this. I didn't even notice he was leaking memory on that `a` decl+initializer. Nice answer, btw. +1 – WhozCraig Oct 16 '13 at 03:06
  • @abarnert You should decide. Either that `a = do();` creates a copy anyway or one can later delete an original object. Not both. – Serge Seredenko Oct 16 '13 at 03:06
  • @SergeSeredenko: As it is in the original post, he's creating a copy, _and_ leaking the original. He has to fix the second. He doesn't have to fix the first. – abarnert Oct 16 '13 at 03:11
  • 1
    @SergeSeredenko: I realize it was a bit hard to follow in the original version. Is it clear enough after the latest edit? Thanks. – abarnert Oct 16 '13 at 03:15
  • @abarnert He didn't leak anything. You mentioned yourself that he could take pointer and delete it. – Serge Seredenko Oct 16 '13 at 03:20
  • @abarnert I am sorry, you were right about leaking it, just tested. You should remove that "* It's not impossible to..." part. It is. – Serge Seredenko Oct 16 '13 at 03:29
  • @SergeSeredenko: You _can_ delete it the way you suggested, as long as you store the reference. (See [here](http://pastebin.com/MnPju5c7).) But obviously not if you store a copy and lose the reference. So, I suppose the most accurate thing to say is that he needs to fix _at least one_ of the two problems (and also do something awkward if he only fixes the second), instead of saying that he needs to fix the second one. – abarnert Oct 16 '13 at 03:36
  • @abarnert Yeah, I got it. Somehow I was absolutely sure that defining function as `Myclass& do()` would be enough to tell compiler not to copy object on assignment after return. I was wrong. – Serge Seredenko Oct 16 '13 at 04:11