views:

184

answers:

5

I'm moving from Java to C++ and am a bit confused of the language's flexibility. One point is that there are three ways to store objects: A pointer, a reference and a scalar (storing the object itself if I understand it correctly).

I tend to use references where possible, because that is as close to Java as possible. In some cases, e.g. getters for derived attributes, this is not possible:

MyType &MyClass::getSomeAttribute() {
    MyType t;
    return t;
}

This does not compile, because t exists only within the scope of getSomeAttribute() and if I return a reference to it, it would point nowhere before the client can use it.

Therefore I'm left with two options:

  1. Return a pointer
  2. Return a scalar

Returning a pointer would look like this:

MyType *MyClass::getSomeAttribute() {
    MyType *t = new MyType;
    return t;
}

This'd work, but the client would have to check this pointer for NULL in order to be really sure, something that's not necessary with references. Another problem is that the caller would have to make sure that t is deallocated, I'd rather not deal with that if I can avoid it.

The alternative would be to return the object itself (scalar):

MyType MyClass::getSomeAttribute() {
    MyType t;
    return t;
}

That's pretty straightforward and just what I want in this case: It feels like a reference and it can't be null. If the object is out of scope in the client's code, it is deleted. Pretty handy. However, I rarely see anyone doing that, is there a reason for that? Is there some kind of performance problem if I return a scalar instead of a pointer or reference?

What is the most common/elegant approach to handle this problem?

+8  A: 

Return by value. The compiler can optimize away the copy, so the end result is what you want. An object is created, and returned to the caller.

I think the reason why you rarely see people do this is because you're looking at the wrong C++ code. ;) Most people coming from Java feel uncomfortable doing something like this, so they call new all over the place. And then they get memory leaks all over the place, have to check for NULL and all the other problems that can cause. :)

It might also be worth pointing out that C++ references have very little in common with Java references. A reference in Java is much more similar to a pointer (it can be reseated, or set to NULL). In fact the only real differences are that a pointer can point to a garbage value as well (if it is uninitialized, or it points to an object that has gone out of scope), and that you can do pointer arithmetics on a pointer into an array. A C++ references is an alias for an object. A Java reference doesn't behave like that.

jalf
Thanks, so there is no real caveat of returning a derived attribute by value? Great :)How about returning a member by value instead of by reference how I do it currently? Or passing parameters by value. Is this inefficient?
theone
There are caveats pretty much everywhere in C++. But as long as you keep the obvious in mind, that you're returning *a copy*, rather than the object itself, and as long as your copy constructors are correctly written, it should be fine.
jalf
As for passing parameters by value, it depends. Once again, the compiler is allowed to eliminate the copy (and it typically does so aggressively). Further, there are cases where pass-by-copy may be *faster* ( http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ ). But often, people pass by const reference instead of trusting the compiler to optimize away the copy.
jalf
I thought the compiler was required to make a copy of an object if the code states so. If my compiler would not make a copy of an object that I pass by copy, I would be very surprised. Are you sure that compilers optimize this aggressively? If so, when? I do agree though that passing by value instead of reference/pointer can be much faster for small sizes or for hardware-optimized types (such as vectors).
Simon
@Simon: compilers are explicitly allowed to perform copy elision **even** if the copy constructor it is bypassing has side effects. Yes, it is surprising, but it is one of the few optimizations *explicitly* mentioned and allowed in the language standard.
jalf
Simon
Returning copies only works reliably if you don't need polymorphy though. Once method A giveMeAnA() should also allow returning instances of subclasses of A, you need to return a pointer (or smart pointer like shared_ptr), otherwise the object will be sliced.
Frank
@Simon: what choice? All that happens is that you get the "choice" of disabling an optimization". Copy elision still gives you a new object, so logically, it looks exactly as if it were copied. You're drawing the wrong conclusion. It's not "I need a way to *force* a copy", but rather "I shouldn't be silly and put side effects in my copy constructor".
jalf
@Frank: true enough
jalf
@jalf: Ah, I see now - so it eliminates the copy and calls a direct initialization instead? Then it makes sense, thanks! Still, direct-initialization is still the same in performance as copy-initialization so if you have big objects (with expensive initialization) wouldn't it make sense to use const-references?
Simon
+3  A: 

Quite simply, avoid using pointers and dynamic allocation by new wherever possible. Use values, references and automatically allocated objects instead. Of course you can't always avoid dynamic allocation, but it should be a last resort, not a first.

anon
A: 

Returning by value is a common thing practised in C++. However, when you are passing an object, you pass by reference.

Example

 main()
   {

       equity trader;

       isTraderAllowed(trader);

       ....
    }

    bool isTraderAllowed(const equity& trdobj)
    {
             ... // Perform your function routine here.
    }

The above is a simple example of passing an object by reference. In reality, you would have a method called isTraderAllowed for the class equity, but I was showing you a real use of passing by reference.

A: 

Returning by value can introduce performance penalties because this means the object needs to be copied. If it is a large object, like a list, that operation might be very expensive.

But modern compilers are very good about making this not happen. The C++ standards explicitly states that the compiler is allowed to elide copies in certain circumstances. The particular instance that would be relevant in the example code you gave is called the 'return value optimization'.

Personally, I return by (usually const) reference when I'm returning a member variable, and return some sort of smart pointer object of some kind (frequently ::std::auto_ptr) when I need to dynamically allocate something. Otherwise I return by value.

I also very frequently have const reference parameters, and this is very common in C++. This is a way of passing a parameter and saying "the function is not allowed to touch this". Basically a read-only parameter. It should only be used for objects that are more complex than a single integer or pointer though.

I think one big change from Java is that const is important and used very frequently. Learn to understand it and make it your friend.

I also think Neil's answer is correct in stating that avoiding dynamic allocation whenever possible is a good idea. You should not contort your design too much to make that happen, but you should definitely prefer design choices in which it doesn't have to happen.

Omnifarious
Great advice, thank you. I really love const, I can't belive how I lived without it. Yet I still have problems to decide when to use it. For instance: I have a class that encapsulates good old procedural OpenGL calls. If a method does OpenGL stuff, is it const or not? I mean, it does alter some state somewhere in the program.
theone
@theone: the usual issue is "does it alter the *logical* state of the object it is a member of". If you give me a const OpenGL object, which operations would it make sense for me to perform? Don't be too fussed about whether it alters some bit of state somewhere.
jalf
@theone, @jalf - I would add that "alter the logical state" should have a formal definition of "Does it alter the semantic result (i.e. it might alter the amount of time it takes, but not what it does) of any subsequent call to a member function of the same object?".Of course, in certain circumstances, `const` will have a stricter definition because the object's state may be in memory that is actually non-writeable.
Omnifarious
@Omnifarious: but that can be worked around by marking one or more members as `mutable`.
jalf
What I don't like about returning const refs is that you _must_ return a member then. You can't return a temporary, computed value, delegate the call etc. anymore. You expose an implementation detail and lose flexibility in the implementation. So at least when the signature cannot be changed at will later (i.e. when writing a library), return by value is the saner option to me.
Frank
@Frank - That's a really interesting perspective, and makes a lot of sense. I will reconsider returning a reference from now on, because you're right, it represents a huge constraint on the future implementation of a library.
Omnifarious
A: 

A point regarding passing by value or reference:
Considering optimizations, assuming a function is inline, if its parameter is declared as "const DataType objectName" that DataType could be anything even primitives, no object copy will be involved; and if its parameter is declared as "const DataType & objectName" or "DataType & objectName" that again DataType could be anything even primitives, no address taking or pointer will be involved. In both previous cases input arguments are used directly in assembly code.

A point regarding references:
A reference is not always a pointer, as instance when you have following code in the body of a function, the reference is not a pointer:

int adad=5;
int & reference=adad;  

A point regarding returning by value:
as some people have mentioned, using good compilers with capability of optimizations, returning by value of any type will not cause an extra copy.

A point regarding return by reference:
In case of inline functions and optimizations, returning by reference will not involve address taking or pointer.

Pooria
All the bold really detracts from what you're trying to say.
Dennis Zickefoose