Jump to content
  • Advertisement
Sign in to follow this  
RealMarkP

Should I Return a const reference

This topic is 3711 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to optimize some of my code, and I was wondering if its worth returning a const reference over a copy of the object I am returning? Should I be worried that people can try to cast it to a non-const object and meddling with the data? Example:
#include "MyOtherClass.h"

class MyClass
{
  MyOtherClass foo;
public:
  // ... code ...
  const SomeOtherClass &myFunciton()
  {
    return foo;
  }
}

Also, Returning references to local values is our of the question... Getters/Setters only apply? EDIT: Spelling nazis attacked me.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by RealMarkP
I'm trying to optimize some of my code, and I was wondering if its worth returning a const reference over a copy of the object I am returning?


Yes, if copying that object is the bottleneck you found by profiling the code.

Quote:
Original post by RealMarkP
Should I be worried that people can try to cast it to a non-const object and meddling with the data?


Yes, but it's their problem.


Quote:
Original post by RealMarkP
Also, Returning references to local values is our of the question...


Correct

Quote:
Original post by RealMarkP
Getters/Setters only apply?


What?

Share this post


Link to post
Share on other sites
I'm in the same boat as to the getters / settings.

<source lang="C++">
void Class::setFont(Font* font) {
this->m_font = font;
}

Font* Class::getFont() const {
return this->m_font;
}
</source>

OR

<source lang="C++">
void Class::setFont(const Font& font) {
this->m_font = &font;
}

Font& Class::getFont() const {
return *this->m_font;
}
</source>

Share this post


Link to post
Share on other sites
Quote:
Original post by RDragon1
Quote:
Original post by RealMarkP
I'm trying to optimize some of my code, and I was wondering if its worth returning a const reference over a copy of the object I am returning?


Yes, if copying that object is the bottleneck you found by profiling the code.



Good point. Ill probably leave this to a last-resort scenario where my copy constructor can't be optimized further.

Share this post


Link to post
Share on other sites
No. You shouldn't do this for a number of reasons.

First of all, you get no optimization in the typical usage case:


const Foo& getValue(){...}
Foo f = getValue();


In the above snippet, the function returns a const reference but the l-value is declared by value. This means the copy constructor is invoked and hereafter you're dealing with a copy of the member anyways.

There are only two good reasons to return reference types from functions:

(1) So functions can be l-values. Ex:

Foo& Foo::operator=(const Foo& rhs){...};
...
Foo a, b, c;
a = b = c; // Expands to (a=(b.operator=(c)));


(2) So we can use value semantics on functions

const Bar& Foo::operator[](std::size_t idx){...};
...
Foo a;
...
a[24].doStuff(); // Call Bar::doStuff() const


If you are not doing either (1) or (2), do not return a const or non-const reference from a function.

Returning references to members breaks encapsulation, which is entirely contrary to OOP. An object's encapsulation is proportional to how many functions can access its internals. Every time you hand out a handle to an object's internals you are breaking encapsulation and increasing coupling.

I won't even get into the headaches this causes in a multi-threaded environment.

Most importantly you shouldn't be afraid of returning non-trivial objects by value. Any extra cost incurred by return-by-value is trivially optimized through NRVO/RVO Optimization and every respectable C++ compiler does it. So if you need to return a copy, just return a copy.

[edit]

Fixed associativity error when expanding operator=()

[Edited by - fpsgamer on June 16, 2008 10:17:34 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by fpsgamer
Returning references to members breaks encapsulation, which is entirely contrary to OOP.


Please explain how returning a constant reference breaks encapsulation. In particular, please provide an example of how you could use the returned object in a way that is both legal (that is, it doesn't make any assumptions beyond the fact that it's a constant reference) and would prevent me from later changing the return value to a non-reference object instead.

Share this post


Link to post
Share on other sites
I don't know about breaking encapsulation but it does restrict your implementation a little. It prevents the possibility of having this function derive the return value from other member variables to return the result because you're forced to return a reference to something that exists for at least the lifetime of the class instance, i.e. probably a member variable.

e.g.
class foo() {
int x, y;
int getX() { return x; }
int getY() { return y; }
};

class bar() {
int x, y;
const int &getX() { return x; }
const int &getY() { return y; }
};
No problems so far, but now we decide to skew the coordinate system returned, like this:
class foo() {
int x, y;
int getX() { return x + y; }
int getY() { return x - y; }
};

class bar() {
int x, y;
const int &getX() { return x + y; } // uh oh can't do this!
const int &getY() { return x - y; } // uh oh can't do this!
};
'foo' is fine, but 'bar' wont compile and you're forced to change the class interface.
This may be what fpsgamer was getting at.

Share this post


Link to post
Share on other sites
Quote:
Original post by fpsgamer


There are only two good reasons to return reference types from functions:

(1) So functions can be l-values. Ex:

Foo& Foo::operator=(const Foo& rhs){...};
...
Foo a, b, c;
a = b = c; // Expands to (a.operator=(b)) = c;



I would agree that this is a useful use but the example is not quite right. If that statement expanded this way then a would first be set equal to b and then to c, leaving b unmodified.
In fact it seems that you could just as well return by value and operator chaining would still work. So a reference is returned for performance only (as OP asked)?

Quote:

(2) So we can use value semantics on functions

const Bar& Foo::operator[](std::size_t idx){...};
...
Foo a;
...
a[24].doStuff(); // Call Bar::doStuff() const


Again, this is something that would work as well if a copy was returned (since a const function is called, it doesn't even matter much if it is a copy or not). So again you return a reference only for performance? (If this was the OP's main usage case, would you say he's doing something bad?)

I agree that returning references to members limits your implementation choices. For example, the wxWidget's library returns large things by pointer (to allow returning NULL), and it also wants to keep the returned objects polymorphic.

I guess returning references/pointers to large objects can be a valid optimization, but you should be aware of the pros and cons and consider that it won't pay off for small objects.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Please explain how returning a constant reference breaks encapsulation.

Store it as a const reference, destroy the object that you got it from, access the reference. The internal lifetime of that object is now very much on display. But this is a minor nitpick... he who stores a returned reference without thinking about lifetime management has forgotten what language he's programming in. I agree: Encapsulation is not a good reason to avoid returning const references.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!