2

What are the difference between a public class member

class data
{
public:

    std::list<data> list_of_data;
};

And a method that returns a private member as reference?

class data
{
public:

   std::list<data>& get_data()
   { return list_of_data; }

private:

    std::list<data> list_of_data;
};

Which one is better?

Nick
  • 10,309
  • 21
  • 97
  • 201

5 Answers5

3

The question doesn't make much sense in that form. If you publicly broadcast the fact that in the second case the list_of_data is indeed a member of data (even though it is private), then there's really no difference.

But that's not what we have private members for. In typical C++ design, in your second variant the outside code is not allowed to use any knowledge about private members of data. The only thing the outside code has knowledge about is the public get_data() member. Where that get_data() goes for the actual data - the outsiders don't know and don't care.

In this case the difference becomes quite noticeable.

  • In the first case you are exposing the fact that list_of_data is physically present as a member of class data. For example, it immediately means that the lifetime of list_of_data is the same as lifetime of the corresponding data instance. It also means that different data instances have different list_of_data members.

  • In the second case you are not exposing anything like that. The outside code does not know where the actual std::list<data> object is located and what's its lifetime. The outside code does not know whether different instances of data will return different references from their get_data() members. In order to get answers to these questions the outside users have to pay attention to the intended design of the code, instead of jumping to conclusions by reading the code. And it is a good thing.

This is the whole reason we often use accessor functions (even reference-returning ones) instead of exposing data members publicly.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • And where could be located my list_of_data to have for example a different lifetime? – Nick Jul 04 '12 at 17:35
  • @Nick: It depends on your intent. It could be a global variable (for one bad example). It could be dynamically allocated for another example and shared by many `data` objects. It could be owned by some completely different object, referred from `data`. Possibilities are endless. – AnT stands with Russia Jul 04 '12 at 17:37
  • @Nick: Also, the point here is that today it might be on one place, tomorrow - in a completely different place. To make such change all you need is to update `get_data`. No other changes are necessary. – AnT stands with Russia Jul 04 '12 at 17:38
2

The second approach is almost always more desirable. It adds a little extra abstraction that is just enough to be quite useful.

For example, with the second approach, you can move your member variable to any other composite inside your class and still maintain the interface. You can even move it to a PIMPL and drop the requirement to include <list>. Or you can make the function virtual and move the member elsewhere. It makes basically all the code that uses your class more resistant to change, which is always good.

In its most basic form, the function is usually even inlined, making the abstraction free during runtime.

Now the only aspect that isn't really free is programmer-time, and if you think that it is more cost effective to leave out writing the function, so be it. But then you should probably use one of the "dumb" aggregate types, such as tuple<> or pair<>

ltjax
  • 15,837
  • 3
  • 39
  • 62
0

If it`s not struct (i.e. only data without any logic) - members must be private. For explanation read Herb Sutter for example.

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • 4
    Not a very good answer. They don't "have" to be private. – someguy Jul 04 '12 at 17:11
  • @someguy, I`m disagree. Can you explain your opinion? – ForEveR Jul 04 '12 at 17:16
  • Besides not giving any sort of explanation and only referring to Herb Sutter without any links, the way you say it is as if it's some sort of rule. I could define a class with one public member and the rest private because maybe I want that member to be modified by any function. Can you really argue with that without asking how I designed that class? – someguy Jul 04 '12 at 17:23
  • What do you mean, it's not "only data without any logic"? That's exactly what the first example is, and the second doesn't really add any logic. Keeping data members private is indeed a good guideline; but it's just a guideline, and adding a function that gives direct access to it is no better than making the data member public. – Mike Seymour Jul 04 '12 at 17:33
  • @Mike Seymour: I believe he was referring to aggregates, which don't have any behaviour and only represent data for outside functions to access. – someguy Jul 04 '12 at 17:36
  • @someguy: That's exactly what the first example is. – Mike Seymour Jul 04 '12 at 17:37
  • @someguy, I think that public fields should be only if this type has no any logic. If type has private data-members and also have non-static public data-member, it`s bad designed. @MikeSeymour, "only data without logic" - some type with data-members and no member-functions, which works with type data-members. – ForEveR Jul 04 '12 at 17:50
0

Both approaches are bad encapsulation, because they can modify the object and even replace it by a new object. Instead, use a get method that returns an object by value, or a const reference:

const MyClass &getObj() { .... }

so that the returned object cannot be modified.

EDIT If you still want to modify the returned object, then just return it by reference. This is, in my mind, at least better than public field.

  • Don't say that one thing is bad without explaining what would be best to do – Nick Jul 04 '12 at 17:12
  • An object by value could be very inefficient, and if I return a const reference I cannot modify it. – Nick Jul 04 '12 at 17:16
  • 2
    You can "replace" an object even if you only have a reference to it. – ltjax Jul 04 '12 at 17:21
  • 1
    @Desolator you are wrong: `data d; auto l = &d.get_data(); l = new list();` – Nick Jul 04 '12 at 17:22
  • @Nick: returning by value isn't necessarily inefficient. Any decent compiler can perform return value optimization. – someguy Jul 04 '12 at 17:28
  • @Nick How do you assign a new object to reference `l` by using `new` keyword?? Are you talking about `C++` or `C#\Java`?? Even if `l` is a pointer, this doesn't replace the field. –  Jul 04 '12 at 17:28
  • @Desolator: notice the address-of operator, but bear in mind he meant Itjax. – someguy Jul 04 '12 at 17:30
  • @Desolator: d.get_data() = std::list(); same thing you can do from inside the class.... – ltjax Jul 04 '12 at 17:31
  • @Itjax: have you honestly tested that, because IIRC, that does not work. – someguy Jul 04 '12 at 17:33
  • 1
    @someguy it's inefficient enough and more inefficient that return a list by reference. – Nick Jul 04 '12 at 17:48
  • @Nick: No, it isn't... did you read what I said? – someguy Jul 04 '12 at 18:34
0

Let me refer you to a couple of articles:

gotw #70 - encapsulation Deals with Questions about data members being public, protected, private.

gotw #76: uses and abuses of access rights

After reading Herb Sutter's Exceptional C++ style which contains several gotw articles (including the ones above), I definitely vote for your 2nd example: private members, public access methods.

Carsten Greiner
  • 2,928
  • 2
  • 16
  • 20