12

In C++, it is possible to create an accessor which returns a reference to a private field.

class Cls {
    private:
        int _attr;
    public:
        int& attr() { return _attr; }
};

such that the attribute can be accessed as such:

// set
c.attr() = 4;

// get
cout << c.attr() << endl;

Is this style of accessor idiomatic/good practice? Would an average C++ programmer be surprised seeing this style of accessor? (Hint: I was surprised the first time I saw this, but kind of liked the style)

Lie Ryan
  • 62,238
  • 13
  • 100
  • 144
  • 7
    Difficult to answer. Average programmers don't usually come to SO. Just the extremes of the spectrum ;) – Jon Mar 09 '11 at 17:47
  • 1
    Given that many stdlib containers do this for various operators, I would hope (not expect) that average c++ programmers had no issue with this. – Erik Mar 09 '11 at 17:50
  • 3
    @Erik - Not usually for members. Containers do it for `operator[]` but that's expected due to the way the operator is already used for arrays and pointers. But `std::string` doesn't let you say `s.size() = 10;`. If you want to resize a string, you do it with `s.resize(10);`. – Chris Lutz Mar 09 '11 at 17:56

8 Answers8

10

Suppose you needed guarantees:

template <int Base>
class Digit {
  private:
    int attr_; // leading underscore names are reserved!
  public:
    int attr() const { return attr_; }
    void attr(int i) { attr_ = i % Base; } // !
};

In that case, you can't return a reference, since there's no way to guarantee that the user won't modify it in a way that you don't want. This is the main reason for using a setter - you can (now or at a later date) filter the input to make sure it's acceptable. With a reference you have to blindly accept what the user assigns to your member. At that point, why not just make it public?

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
  • 3
    actually, leading underscore names are not all reserved. They are reserved within the global namespace (which is not the case) and they are reserved if the letter following them is upper case (which is not the case either). The use of `_[a-z0-9].*` identifiers for class members is fine. +1 for the answer though, if you provide direct access, you've broken encapsulation anyway. – Matthieu M. Mar 09 '11 at 18:20
  • @Matthieu M. - I'm not sure what subset of `_[a-zA-Z0-9_]*` names are reserved for implementation macros, but macros can't be scoped, so you'd have to avoid having any `__DATE__` members. I predominantly use C, though, so I tend avoid reserved names regardless of scope. – Chris Lutz Mar 09 '11 at 18:25
  • 1
    @Matthieu: Beat me to the punch. Here is a reference for anyone trying to find it in the standard: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier – Zac Howland Mar 09 '11 at 18:28
  • 1
    `_[A-Z].*` and `.*__.*` are reserved for any use. Anything else beginning with `_` is reserved for use as a name in the global namespace. – Mike Seymour Mar 09 '11 at 19:26
7

No, that code would be unsurprising, as long as you also provide a const overload:

class Cls {
    int _attr;
public:
    int& attr() { return _attr; }
    int const& attr() const { return _attr; }
};

However, I would consider the following idiomatic, for the reasons mentioned by Chris Lutz and Mark B:

class Cls {
    int _attr;
public:
    int const& attr() const { return _attr; }
    void attr(int i) { _attr = i; }
};
ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • if you've already read @Chris Lutz's answer, don't you agree with him about broken encapsulation? I'd recommend to remove your answer if you realized it's not right/accurate enough. Especially because of all these upvotes for your misleading answer – Andriy Tylychko Mar 09 '11 at 21:20
  • @Andy T : The question is whether it's idiomatic, not whether it's ideal. I've seen this pattern enough times to say "yes" to that question, even if I don't like it, which is why I pointed out my preferred approach. – ildjarn Mar 09 '11 at 21:41
  • @ildjarn: following your logic any anti-pattern is idiomatic. editing history of your answer shows that you tried to correct your answer. Deleting it would be better because it's still misleading. – Andriy Tylychko Mar 09 '11 at 22:24
  • 1
    @Andy T : My answer was voted up before I edited it -- I didn't make the edit to garner more votes. In many cases, especially with non-POD types (unlike in this example), returning internal data by non-const reference does not break encapsulation at all. Combine that with the fact that this pattern is extremely prevelent, and I stand behind my answer 100%. – ildjarn Mar 09 '11 at 22:41
  • @ildjarn: 1) check [encapsulation](http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)); 2) check other answers and especially @Chris Lutz's comment for the last one. the single reason why this anti-idiomatic practice is so popular is that many people heard it's bad to have public member variables but don't know why – Andriy Tylychko Mar 09 '11 at 22:58
  • 1
    @Andy T : The bottom line is, the question reads "Would an average C++ programmer be surprised seeing this style of accessor?" The answer is: No. Feel free to downvote, but you're not going to change my mind. :-] – ildjarn Mar 09 '11 at 23:01
5

In general, for non-trivial classes (for example ones that can be defined simply as a struct) providing accessors/mutators to specific attributes is probably a code smell. Generally, classes should tend to keep their internal state just that: Internal. If you provide a non-const reference to internal state then all of a sudden you have no control over class invariants at all. This will not only make debugging significantly harder since the scope of possible state changes is the entire project, it prevents you from ever changing the internals of your class because they're actually both state AND user API.

Instead you should probably devise methods that perform meaningful work on your class while maintaining the class invariants. In some cases providing read-only access to specific state variables is perfectly fine while in others you only want to provide access to end state in another way.

Mark B
  • 95,107
  • 10
  • 109
  • 188
5

Doing so completely defeats the purpose of making it private in the first place.

The purpose of an accessor is to expose the internal state in such a way that the class can maintain invariants when external code tries to modify the state (either by only allowing read access, or by validating write access before modifying the state); they will usually look something like

int  attr() const {return _attr;}
void attr(int a)  {_attr = a;}

If, in the future, you need to constrain the values that the attribute can take, or change the way it is stored internally, then you can modify the implementation of these without changing the class interface.

If this is not necessary (and you have decided that it will never be necessary), just make it public; exposing it as a reference gains nothing but a few lines of unnecessary code and a quirky syntax when using it.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
4

It depends. If the role of the class is to contain such objects (e.g. a container class), then its very idiomatic, and the normal way of doing things. In most other cases, however, it is considered preferrable to use getter and setter methods. Not necessarily named getXxx and setXxx---the most frequent naming convention I've seen uses m_attr for the name of the attribute, and simply attr for the name of both the getter and the setter. (Operator overloading will choose between them according to the number of arguments.)

-- James Kanze

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 1
    This is a nice write up (aside from the unnecessary signature) but it's not really adding any information. – Chris Lutz Mar 09 '11 at 18:28
2

Returning references to POD members is generally not too common. I think must readers would expect a setter for this purpose. However, if you do it, don't forget to overload it for the const case as well.

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
2

It's relatively frequent, but it's bad style.

If you have a look at the STL, for example, you'll remark that the class that yields const& or & to internal state are usually plain containers and only provide this kind of access for what you actually store in them. You have, obviously, no way to modify directly attributes such as size or the internal nodes of a binary tree.

Providing direct access to elements breaks encapsulation. Not only do you lose all class invariants wrt to those elements, but you also make them part of your API, and therefore cannot change the implementation of the class without also updating all clients.

As far as I am concerned, they are two types of classes in C++:

struct BigBag {
  Foo _foo;
  Bar _bar;
  FooBar _foobar;
};

class MyClass {
public:
  explicit MyClass(int a, int b);

  int getSomeInfo();

  void updateDetails(int a, int b);
private:
  // no peeking
};

That is: either it's just a collection of related elements, in which case everything is public and you're done, or there are class invariants and/or you're concerned with its API because there's a lot of client code, in which case you do not expose at all the implementation details.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
0

Just in case you want to get rid of the parentheses...

class Cls {
    private:
        int _attr;
    public:
        Cls() : attr(_attr) { }
        int& attr;
};

Edit: Chris' point is a good one. Essentially nothing is gained from wrapping a private variable with a public reference. The following version does add something though. Making the reference const prevents setting but allows getting of the private variable.

class Cls {
    private:
        int _attr;
    public:
        Cls() : attr(_attr) { }
        int const & attr;
};
Nick Strupat
  • 4,928
  • 4
  • 44
  • 56