When should you return a reference to a object from a class method

后端 未结 7 1752
旧时难觅i
旧时难觅i 2021-02-14 01:14

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 r

7条回答
  •  深忆病人
    2021-02-14 01:54

    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 for implementation (and that's what your getter returns). User code like "vector foo = obj.getItems()" appears. Then you change your implementation (and getter) to use a deque -- 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::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.

提交回复
热议问题