• Advertisement
Sign in to follow this  

[C++] Scott Meyers's item 22 - private data members

This topic is 2773 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

Most of the time Meyers writing style is clear and concise, but this item has befuddled me.

He basically states:
Never, ever, use protected data members

and goes on saying that protected data members are as 'un'capsulated as public members and so on...

My question is: how will my derived classes be able to modify its (base) members values then?

I know that a virtual function overridden in the derived class can access its base members, but until now I've been declaring all those base members as protected, whenever I want to change them through the derived class... Now, if they are private to the base class, how will my derived class be able to change it?

is it something to do with the way I inherit the base class?

like using class Derived : public Base {} ?

thanks!

Share this post


Link to post
Share on other sites
Advertisement
I believe his logic is to have getters and setters defined in the base class (which those functions could be declared protected if you wish), so the base class always defines the control over it's members.

Share this post


Link to post
Share on other sites
Quote:
My question is: how will my derived classes be able to modify its (base) members values then?
Your derived classes can interact with the base class in the same way that non-derived classes do - through the presented interface. You should need to use protected about as often as you use friend -- to punch a sneaky back-door through your safe/sanity-checked interface.

The fact that you're asking that question indicates that your base class does not provide a full and complete abstraction - some details of it's inner workings are un-implemented, which makes the class harder to be proven to be correct. Often this means that the base class exists only to facilitate code-re-use (via inheriting this common code into the derived classes), which is often better achieved via composition instead.
Quote:
Original post by Rattrap
I believe his logic is to have getters and setters defined in the base class (which those functions could be declared protected if you wish), so the base class always defines the control over it's members.
That's better than nothing, but if you've got a class that provides direct access to it's members via set/get methods, then it's sometimes an indication that the class is not a very good abstraction of whatever problem it's designed to solve.

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
Quote:
My question is: how will my derived classes be able to modify its (base) members values then?
The fact that you're asking that question shows that your base class does not provide a full and complete abstraction.

Your derived classes can interact with the base class in the same way that non-derived classes do - through the presented interface. You should need to use protected about as often as you use friend -- to punch a sneaky back-door through your safe/sanity-checked interface.


ooookays. but how do I accomplish this? by using getters & setters in the base class?

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
Without knowing what your problem is, I would guess that the answer is "don't use inheritance"...


It is no really a problem in my code/project. I'm trying to understand why Meyers proposes something, not using protected members, but does not offer an example of the "right way" to do it. Maybe he does in another item, but I couldn't find it yet :)

And then, it was so natural for me the use of protected data members ( I usually work with no more than 1 level of inheritance) that stroke me as odd that using it was bad design.

Share this post


Link to post
Share on other sites
The first thing that pops to my mind is that direct access to the variable would be scattered around the code. Not only that but if someone else than you derives your class, and have direct access to your variable you are not even in control of when, where and how this variable are accessed.
Using getter/setter functions will make sure direct access happens at one and only one place in the code, and any changes to how the getter/setter functions access the variable will affect all clients immediately.
Not sure if that is what Meyers had in mind but its a pretty good reason in itself I would say.

Share this post


Link to post
Share on other sites
I think I see what he meant.

at the very first pages he says something about programming "to a library" (can't remember the exact words), but it was something on the way of "think as if people will use your code to complement/enhance theirs".

And yes, now I see that having protected variables running around some code that I am don't even aware of can for sure damage many things whenever I a change to the base class is needed.

Share this post


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

Using getter/setter functions will make sure direct access happens at one and only one place in the code, and any changes to how the getter/setter functions access the variable will affect all clients immediately.

Getters and setters are the same as just making variables public. It even saves typing.

Getters/setters became so popular because of deficiencies of Java as a language when tackling its most common problem - ORM. And since Java is a language just about everyone must learn, they became the magic bullet to everything.


struct FooBar {
void moveTo(int x,int y);
void jump();
void climb();
void render(Canvas & c) {
c.draw_rect(this->x, this->y, this->width, this->height);
}
}
There, no need for any accessors or such. That is what OO should aspire to. Note that no variables whatsoever are visible, everything else is public. Any class extending this cannot break the underlying class, nor does it depend on non-public specifics.

It is not always a silver bullet though, sometimes it's practical to violate some of these principles. And some types of functionality are simply too trivial to OO-ify, such as geometries (rect, point, ...), or trivial value types (name/value pairs, strings, ...).

Don't go OO crazy with Point or Rect class. Reserve it for complex objects and actions that express a lot of logic via simple interface.

Share this post


Link to post
Share on other sites
Think about the reason you would make any data member non-public: there's a class invariant that you want to maintain. If the variable is private then only the member functions of that class are responsible for maintaining the class invariant. If the variable is protected then not only are the member functions of the class responsible for maintaining the invariant, but all of the member functions of all the derived classes become responsible for maintaining the class invariant. This is generally a sub-optimal way of managing things.

Share this post


Link to post
Share on other sites
I think getters/setters are useless if you are just assigning or returning the value like :

int getX(){return x;}
void setX(int x){this->x = x;}

Why not just make them public ? If there is no need to validate x i just make it public.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Think about the reason you would make any data member non-public: there's a class invariant that you want to maintain. If the variable is private then only the member functions of that class are responsible for maintaining the class invariant. If the variable is protected then not only are the member functions of the class responsible for maintaining the invariant, but all of the member functions of all the derived classes become responsible for maintaining the class invariant. This is generally a sub-optimal way of managing things.


sorry about my ignorance. But could you please give me a concrete example of what a class invariant is?

everybody talks about this person, but I was never introduced to him :D

Share this post


Link to post
Share on other sites
Quote:

Getters and setters are the same as just making variables public

Hodgeman already explained how setters and getters could imply bad design and I don't dispute that.
Still it depends what the variable represents. While accessing an objects positional components (Illustrated by Black Knight) is unlikely to change, access to other variables may, or even should, change in the future.

Share this post


Link to post
Share on other sites
Quote:
But could you please give me a concrete example of what a class invariant is?
Say you have a class that contains a dynamic array of objects, and also an unsigned integer index that indicates which of these objects is currently active or selected. A reasonable class invariant in this case would be that unless the array is empty, the value of the index is less than the size of the array.

Share this post


Link to post
Share on other sites
A class invariant is a non-trivial statement about a class' data members that is always true. For example a 3D vector has no meaningful invariants. You've got an X, a Y and a Z but any can assume pretty much any value, so there's no reason not to just make them public. However, a normalized 3D vector does have a meaningful invariant. X2 + Y2 + Z2 is equal to 1 (or at the very least is very, very close to 1). You wouldn't want X, Y and Z to all be public here because someone could change one of them and all of the sudden your vector wouldn't be normalized.

Another example: let's say you have an RPG creature with hit points. Your rule system might say that current hit points should always be greater than or equal to 0 and less than or equal to the max hit points.

Share this post


Link to post
Share on other sites
You will also note that the item is part of a greater theme: Classes should do stuff. Structures and POD classes should just store stuff.


Antheus started along that explanation with his example.


public:
// Stuff the class can do.
// Generally a verb or verb-noun pair

void moveTo(int x,int y); /* Does most of the work, but also calls findPath(), maxJumpDistance, and other protected virtual functions */
void climb() { doClimb() }; /* Passes the work off to the derived class */
void jump();
void render(Canvas & c);
...
const Vec3f& getLocation( return m_location) const; // accessor
void setLocation( Vec3f location ); // mutator

protected:
// Details about how things get done that a subclass can override
virtual Path& findPath();
virtual void doClimb();

// Values that subclasses can override
virtual float maxJumpDistance();
virtual float maxSpeed();
virtual bool shouldLoop();
...

private:
// Data that this class needs to know about
Canvas m_cachedImage;
Path m_path;
Vec3f m_location;
...
};




That's generally what you want a class interface to look like. You don't care how the thing implements jump, or how it implements climb, or how it implements render. All you care is that the object will do its thing.

Observe that the class doesn't provide raw access to the private data, but you do allow some indirect access through the mutator/accessor pair. Since it accesses class-specific variables it should not be virtual.


A generally accepted good c++ design is to have a non-virtual public interface as much as possible.

Those public functions will either implement the code directly, or implement it with protected virtual function calls to the details, or simply delegate all work to the protected virtual functions that are specialized as needed. The data is kept private.

You can implement most of the algorithms in the base class. If something changes in subclasses then you call the protected virtual function to handle it.

Share this post


Link to post
Share on other sites
Quote:
Original post by draconar
Quote:
Original post by Hodgman
Without knowing what your problem is, I would guess that the answer is "don't use inheritance"...


It is no really a problem in my code/project. I'm trying to understand why Meyers proposes something, not using protected members, but does not offer an example of the "right way" to do it.


The right way is to NOT do it, where the 'it' is "how will my derived classes be able to modify its (base) members values then?" This is also why Hodgman has asked you what exactly you are trying to achieve, so that a better way can be suggested.

When you write a base class, you write it with the idea that it stands alone and that all the code in it is correct and consistent. Now imagine that someone can add a class derived from it and fiddle with your data members at any time - suddenly the correctness of your code becomes useless. That's why you need to lock the internals behind member functions, as you can add logic to ensure any changes to your data are correctly handled.

I'd like to add that the people saying that getters and setters are bad design in C++ are missing something important - sometimes you do just have some very simple values that you want to use, but we don't have anything in C++ that gates the access to those values, such as properties in VB/Python/C#/etc. In that case, simple getters and setters provide a clean interface which doesn't have to change later if you alter the representation. But of course, 95% of the time you don't truly want getters and setters, you want a higher level behaviour that does something with those values. And of the other 5% of the time, mostly you only need getters and no setters anyway. But don't rule them out as a legitimate interface if that is indeed the kind of access you need.

Share this post


Link to post
Share on other sites
Nice indeed. Think your arguments are much better than Meyers's "never ever use protected member variables."

I will need some time to digest the exposition you guys made, but I think I'm in solid ground now.

thank you all!

Share this post


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

  • Advertisement