tags:

views:

1401

answers:

7

What is the best practice for returning references from class methods. Is it the case that basic types you want to return without a reference whereas class objects you want to return by reference. Any articles, best practices article that you recommend.

+2  A: 

A reference is a pointer in disguise (so it's 4 bytes on 32 bit machines, and 8 bytes on 64 bit machines). So the rule of thumb is: if copying the object is more expensive than returning a pointer, use a pointer (or reference, since that's the same thing).

Which types are more expensive to copy depends on the architecture, compiler, the type itself etc. In some cases copying an object that's 16 bytes can be faster than returning a pointer to it (for example, if an object maps to SSE register, or similar situation).

Now, of course, returning a reference to a local variable does not make sense. Because the local variable will be gone after the function exits. So usually you'd return references/pointers to member variables, or global/static variables, or dynamically allocated objects.

There are situations where you don't want to return pointer/reference to an object, even if copying the object is expensive. Mostly when you don't want to tie the calling code into the lifetime of the original object.

NeARAZ
+2  A: 

Overloading assignment operators (like =, +=, -= etc.) is a good example where returning by reference makes a lot of sense. This kind of methods would obviously return large objects, and you don't want to get back pointers, so returning a reference is the best way to go. Works like a pointer and looks like returning by value.

Eduard - Gabriel Munteanu
Meyers in Effective C++ explains in detail.
Calyth
+3  A: 

I'll assume that by class method you mean member function. And that by "return by reference" you mean "return reference to member data". This is mainly as opposed to returning a reference to local, which is clearly wrong.

When should you return a reference to member data, and when the data itself ?

By default, you should be returning the data itself (aka "by value"). This avoids several problems with returning a reference:

  • Users storing the reference and becoming dependant on the lifetime of your members, without considering how long the containing object (your object) will live. Leads to dangling pointers.

  • User code becoming dependant on the exact return type. For example, you use a vector<T> for implementation (and that's what your getter returns). User code like "vector<T> foo = obj.getItems()" appears. Then you change your implementation (and getter) to use a deque<T> -- user code breaks. If you had been returning by value, you could simply make the getter create a local vector, copy the data over from the member deque, and return the result. Quite reasonable for small-sized collections. [*]

So when should you return a reference instead?

  • You can consider it when the returned object is huge (Image) or non-copyable (boost::signal). But, as always, you can instead opt for the more OOP pattern of having your class do stuff rather than have stuff hanging from it. In the Image case, you can provide a drawCircle member function, rather than returning Image& and having your users draw a circle on it.
  • When your data is logically owned by your user, and you're just holding it for him. Consider std collections: vector<T>::operator[] returns a reference to T because that's what I want to get at: my exact object, not a copy of it.


[*] There is a better way to ensure future-proof code. Rather than returning a vector (by ref of by value) return a pair of iterators to your vector -- a beginning and an ending one. This lets your users do everything they normally do with a deque or a vector, but independent of the actual implementation. Boost provides boost::iterator_pair for this purpose. As a perk, it also has operator[] overloaded, so you can even do "int i = obj.getItems()[5]" rather than "int i = obj.getItems().begin()[5]".

This solution is generalizable to any situation which allows you to treat types generically. For example, if you keep a Dog member but your users only need to know it's an Animal (because they only call eat() and sleep()), return an Animal reference/pointer to a freestore-allocated copy of your dog. Then when you decide dogs are weaklings and you really need a wolf for the implementation, user code won't break.

This sort of information-hiding does more than ensure future-compatibility. It also helps keep your design clean.

I used to think that hiding types is useful, but with time this though somehow faded away.I *want* calling code to know the exact types. When changing the implementation, sometimes I deliberately rename the function itself as well, as a way to actually review all calling code. YMMV.
NeARAZ
A: 

I would recommend staying away from returning references for the same reason as Iraimbilanja points out, but IMO you can get very good results by using e.g. shared pointers (boost/tr1) on the member data and use those in return. That way you do not have to copy the object but can still manage life time issues.

class Foo
{
private:
    shared_ptr<Bar> _bar;
public:
    shared_ptr<Bar> getBar() {return _bar;}
};

Usually the cost of copying Bar is greater than the cost of constructing new shared_ptrs, should this not be the case it can still be worth using for life time mgmt.

Fredrik Jansson
A: 

Return basic types by value, except if you want to let the caller access the actual member.

Return class objects (even std::string) by reference.

Igor Oks
A: 

Scott Meyers' book, Effective C++, has several items related to this topic. I would definitely check out Item #23: "Don't try to return a reference when you must return an object."

Kristo
A: 

if NULL is a possible return value, the method must return a pointer because you can't return a reference to NULL.

cd1