Should getters return small, trivial data members by reference or by value?
Asked Answered
B

7

7

If I have a class that has many int, float, and enum data members, is it considered efficient and/or good practice to return them as references rather than copies, and return constant references where no changes should be made, or is there a reason I should return them as copies?

Bartolomeo answered 19/9, 2010 at 5:35 Comment(2)
I asked a related question https://mcmap.net/q/1477173/-c-passing-reference-to-class-39-private-variable-compiler-issue/15161 - maybe the answers there will help.Onia
Wrong question. Better question: Is it a bad idea to have getters to all of your data members?Cenobite
S
8

There is no reason to return primitive types such as int and float by reference, unless you want to allow them to be changed. Returning them by reference is actually less efficient because it saves nothing (ints and pointers are usually the same size) while the dereferencing actually adds overhead.

Steenbok answered 19/9, 2010 at 5:39 Comment(7)
I have a question, though. What should be considered a fair trade-off for using a pointer or reference? For instance, a double is 8 bytes, and a pointer to a double is 4 bytes. Does the added overhead for dereferencing outweigh the smaller size?Bartolomeo
Yes. My rule of thumb is to return primitive types directly, even double. Classes are returned as const references, even small ones.Dichlamydeous
Look at my post which talks about the aspect of simplification of syntax. If 'vector.at' returns a pointer, then we have to write code such as '*(v.at(1)) = 10'. Instead with reference, the syntax is very intuitive 'v.at(1) = 10'. Reference and pointer do not differ in terms of performance / size involved in passing back/forthLinguistics
Reading two consecutive 4-byte locations for a double (or maybe just one on a 64-bit machine) is still going to be faster than reading the pointer and then reading 8 bytes from the target address. Don't go for constant references for anything smaller than, say, 12 bytes.Steenbok
Chubsdad, I understand the distinction between syntax with pointers and references, but my question was regarding efficiency of returning references/pointers versus returning the value itself.Bartolomeo
Is this not something that C++ compilers can figure out? What if you were writing an accessor to a template class?Pensionary
@james: typename boost::call_traits<T>::param_type get_foo();Luthern
A
4

If they are constant references, maybe it is OK. If they are not constant references, probably not.

As to efficiency - on a 64-bit machine, the references will be 64-bit quantities (pointers in disguise); int and float and enum will be smaller. If you return a reference, you are forcing a level of indirection; it is less efficient.

So, especially for built-in types as return values, it is generally better to return the value rather than a reference.

Alarcon answered 19/9, 2010 at 5:41 Comment(0)
L
4

Some cases it is necessary:

Look at overloaded operator[] for any class. It usually has two versions. The mutating version has to return a reference.

int &operator[](int index);           // by reference
int operator[](int index) const;      // by value

In general, It is OK to allow access to class members by trusted entities by a class e.g. friends. In case these trusted entities also need to modify the state, references or pointers to the class members, are the only options one has.

In many cases, references usually simplify syntax e.g where 'v' is STL vector.

v.at(1) = 2 vs *(v.at(1)) = 2;
Linguistics answered 19/9, 2010 at 5:42 Comment(0)
C
1

This is probably mostly a matter of style or preference. One reason to not return references is because you are using getters and setters to allow you to change the implementation of those members, If you changed a private member to another type, or removed it completely because it can be computed, then you no longer have the ability to return a reference, since there's nothing to reference.

On the other hand, returning references for non-trivial types (compound classes) can speed up your code a bit over making a copy, and you can allow those members to be assigned through the returned reference (if desired).

Calvano answered 19/9, 2010 at 5:40 Comment(0)
C
0

Almost, const references are better. For ints and such theres no point because you would want them to be changed or because they are the same size (or nearly) as a reference.

So yes it is a good idea. I prefer another language or to hack away at my own C++ stuff and just allow the var to be public (once again it just my own stuff)

Cristoforo answered 19/9, 2010 at 5:43 Comment(3)
Sure, I could make them public, but some of them shouldn't be publicly accessible since they have certain restrictions for which value they can be given, and so I wrap them with setters/getters to enforce those rules. Thanks for the reply!Bartolomeo
@Jenger: For very simple, self-contained rules, you could let the type system help you. For example, instead of an int digit field and a setter that checks that digits are between 0 and 9, you could write your own digit type that only has the values 0 to 9 and then use a public digit field in your class, without a getter and a setter.Luthern
Jengerer: I am saying in my own code at home i'll do that all over the place because its mine and no one else (no one has to touch it). Also if the class is trivial i'll do it at work and -so far- no one has complained. But i dont really do C++ at workCristoforo
D
0

This is a performance question mostly but from a robustness point of view I would say it's preferably to return values instead of const references. The reason being that even const references weakens encapsulation. Consider this:

struct SomeClass
{
   std::vector<int> const & SomeInts () const;
   void AddAnInt (int i);  // Adds an integer to the vector of ints.
private:
   std::vector<int> m_someInts;
};

bool ShouldIAddThisInt(int i);

void F (SomeClass & sc)
{
   auto someInts = sc.SomeInts ();
   auto end = someInts.end ();
   for (auto iter = someInts.begin (); iter != end; ++iter)
   {
      if (ShouldIAddThisInt(*iter))
      {
         // oops invalidates the iterators
         sc.AddAnInt (*iter);
      }
   }  
}

So in case it makes semantically sense and we can avoid excessive dynamic allocations I prefer return by value.

Dinesen answered 19/9, 2010 at 14:8 Comment(0)
M
0

Getters are for emissions of a class say Exhaust Car.emit(), where the car has just created the Exhaust.

If you are bound to write const Seat& Car.get_front_seat()
to have later sit in the Driver, you can immediately notice that something is wrong.
Correcly, you'd rather write Car.get_in_driver(Driver)
which then calls directly seat.sit_into(Driver).

This second method easily avoids those awkward situations when you get_front_seat but the door is closed and you virtually push in the driver through the closed door. Remember, you have only asked for a seat! :)

All in all: always return by value (and rely on return value optimization), or realize it is time for changing your design.

The background: classes were created so that data can be coupled together with its accessor functionality, localizing bugs etc. Thus classes are never activity, but data oriented.

Further pitfalls: in c++ if you return something by const ref, then you can easily forget it is only a ref and once your object is destructed you can be left with an invalid ref. Otherwise, that object will be copied once it leaves the getter anyway. But unnecessay copies are avoided by the compiler, see Return Value Optimization.

Microseism answered 14/11, 2012 at 18:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.