10

I just wondered if I could bypass using getters if I just allowed a const reference variable, as follows

#include <string>

class cTest
{
    private:
        int m_i;
        std::string m_str;

    public:
        const int & i;
        const std::string & str;

    cTest(void)
    : i(m_i)
    , str(m_str)
    {}
};

int main(int argc, char *argv[])
{
    cTest o;
    int i = o.i; // works
    o.i += 5; // fails
    o.str.clear(); // fails

    return 0;
}

I wonder why people do not seem to do this at all. Is there some severe disadvantage I am missing? Please contribute to the list of advantages and disadvantages, and correct them if necessary.

Advantages:

  1. There is no overhead through calls of getter functions.
  2. The program size is decreased because there are less functions.
  3. I can still modify the internals of the class, the reference variables provide a layer of abstraction.

Disadvantages:

  1. Instead of getter functions, I have a bunch of references. This increases the object size.
  2. Using const_cast, people can mess up private members, but these people are mischievous, right?
Fabian
  • 4,001
  • 4
  • 28
  • 59

3 Answers3

7

Some severe disadvantages indeed (aside from the 2nd disadvantage that you also mention which I also put in the "severe" category):

1) You'll need to supply (and therefore maintain) a copy constructor: the compiler default will not work.

2) You'll need to supply an assignment operator: the compiler default will not work.

3) Think carefully about implementing the move semantics. Again, the compiler default will not work.

These three things mean the const reference anti-pattern you propose is a non-starter. Don't do it!

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 5
    Not to mention the day comes when you want to use a more efficient representation for the data and you can't because you need a valid reference to those types. – chris Dec 11 '15 at 08:05
  • @chris: That is a good point. Even if you have a function returning a const ref, you could always refactor that to return a value copy should your data model change. – Bathsheba Dec 11 '15 at 08:15
2

One advantage of getter functions is that you might at some point in time - want to alter returned value - and without getter function you cannot do it. This scenerio would require you to return non reference actually, which is less common in c++. [edit] but with move semantics instead of references this should be doable[/edit]

You might also want to put a breakpoint into getter function to learn who is reading its value, you might want to add logging, etc. This is called encapsulation.

Other advantage of getter is that in debug builds you can add additional checks/asserts on returned data.

In the end compiler will inline your getter functions, which will result in similar code to the one you propose.

some additional disadvantage:

1) template code will want to get values using function call, ie. size(), if you change it to const& variable then you will not be able to use it in some templates. So this is a consistency problem.

marcinj
  • 48,511
  • 9
  • 79
  • 100
0

If you want to avoid getters and setters, using a const reference member is not the solution.

Instead, you'll want to ensure const correctness on the surrounding struct (which automagically gives you const access to the members), and just let the members be whatever they logically need to be.

Be sure to read up on when getters and setters can, should, or could be switched with public data members. See e.g. this question. Just note that if you change the interface, the heralded dvantage of setters/getters is that calling the getter won't affect call sites. Reality seems to argue otherwise, and e.g. refactoring a member along with all its access points is a trivial operation for any self-respecting C++ code editor.

Although one could argue for encapsulation, I'd more strongly argue for const correctness, which alleviates the need for much encapsulation and really simplifies code quite a lot.

Community
  • 1
  • 1
rubenvb
  • 74,642
  • 33
  • 187
  • 332