C++ private vs. public

Started by
14 comments, last by simpler 13 years, 3 months ago
My book says that one should try to keep all member variables in private in a class. And then you public functions to get them outside the class. I was just wondering what the argument for that is. I've read that it's suppose to be "safer", but I dont understand in what way it's safer and why.

After what I've learned a class should look like this:

class Object{public:	Object(int x, int y, int width, int height, ObjectType type, int ID, bool stati = true);	virtual ~Object();	virtual void draw(void);	virtual void update(float dt);	void updateRect(void);	int getX(void) {return mX;};	int getY(void) {return mY;};	int getHeight(void) {return mHeight;};	int getWidth(void) {return mWidth;};	int getID(void) {return mID;};	int getType(void) {return mType;};	int getStatic(void) {return mStatic;}	RECT getRect(void) {return mRect;};private:	IDirect3DTexture9* mTexture;	RECT mRect;	int mX;	int mY;	int mWidth;	int mHeight;	int mID;	ObjectType mType;	bool mStatic;};


I also will have to 8 more setXXX(...) functions in order to change the member variabels. If the member variables would have been public then I would have 16 less functions to worry about ... Please explain why I shouldn't do it that way.

Thanks,
simpler [smile]
Advertisement
The thinking goes something like this:
Imagine your class has an "area" public member. Now you and your team use the class for a few weeks and in a bunch of places. Then you want to change it to calculate the area instead to save memory - minor nightmare to change all the dependent code. This is even worse if you created a library that people you've never met are using.

Another case is if you had a "height" member, and only weeks later do you realize that if someone puts in a negative value it will corrupt all savegames (or something equally horrible). Now you need to add a check for this case, which requires a method and you have a similar issue to above.

You also have the ability to create read-only members by only implementing a getter, or even a write-only.

Btw, creating getters and setters is so common that most IDEs have a shortcut for creating them. You might want to check if yours does.

EDIT: Also, many modern languages have solved this issue with properties, which have syntax like member variables but are actually methods - C++ doesn't have anything equivalent.
[size="1"]Try GardenMind by Inspirado Games !
All feedback welcome.
[s]
[/s]

[size="1"]Twitter: [twitter]Owen_Inspirado[/twitter]
Facebook: Owen Wiggins

[size="1"]Google+: Owen Wiggins

You really have to question why you are using an object in the first place. If all you are doing is creating mindless setters and getters, it should be a struct, not a class. When building classes, you should be caring about the overall behavior of the object, not its internal components. The class uses its internal data to achieve a higher motive. Make sense?
Amateurs practice until they do it right.Professionals practice until they never do it wrong.
A class should never open its inner workings to outside code, regardless of how simple it might appear. Even basic a basic 'getter' and 'setter' allow you a stronger degree of control than just accessing a variable directly. For example, as soon as a variable is public, it can be both set and retrieved. Turning to methods allows you to decide on possibly including only a 'getter' and NOT a 'setter'.

It also allows for your class the flexibility to change. What if you realized that you need to track every time a variable is requested? If you have a 'getter', you can simply add a line of code in that method to do your tracking, with any other code none the wiser to your change. Doing this to a public variable would be much more difficult.

Imagine you have one variable, dollarsPerHour in your initial design. You then realize down the road that you want to instead have totalDollars and timeWorked.
If you originaly created a getter, getDollarsPerHour, you will simply need to change the method to use the two variables instead of just one. One line of code changed and any code that uses it is none the wiser to your change. If you had used a public variable, imagine trying to rework all of the code that uses your class, and imagine how messy it might get.

Those are just a couple reasons and examples I see.
Objects have behaviour, not data. When you think about an object you shouldn't have to worry about it's internals. Ideally you write the interface that makes sense at the conceptual level, and shouldn't depend on the exact implementation.

Objects move(), they don't setX() or setY(). Objects generally don't change size (unless you are writing Mario or something). If they do then they will have grow() or shrink() member functions, not setWidth() and setHeight(). Objects don't change types, just create a new Object instead. Objects don't generally change from being static to non-static.

Also, don't store the object rectangle as a member variable. Instead of calling updateRect() and getRect(), simple make getRect() dynamically generate the rectangle. That, or only store the rectange and drop the x,y,width,height member variables.

Why is it safer? Well, Objects have these things called "invariants". An invariant is something that is a predicate that is always true. For example, you might say that an objects width and height are always greater than 0. The objects texture might never be NULL.

By making these variables private, you can vastly reduce the amount of code that accesses them, which makes enforcing this invariant easy. Other invariants are those I've mentioned about keeping certain state immutable. You could of course enforce this using the "const" keyword, but that can make it hard to write copyable objects.

A class without invariants isn't a class, it is just a convenient grouping of data.
Quote:Original post by rip-off
Objects move(), they don't setX() or setY(). Objects generally don't change size (unless you are writing Mario or something). If they do then they will have grow() or shrink() member functions, not setWidth() and setHeight(). Objects don't change types, just create a new Object instead. Objects don't generally change from being static to non-static.
QFE.

Most of the time, when you write a class that significantly features get/set methods, you are really writing a Facade.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Thanks everyone for great answers!
Quote:Original post by rip-off
Objects move(), they don't setX() or setY(). Objects generally don't change size (unless you are writing Mario or something).


You are right about, when I think about it I really don't need them.

Quote:Original post by rip-off
Objects don't change types, just create a new Object instead. Objects don't generally change from being static to non-static.


Hmm I feel a bit confused. Lets say I inherit Enemy from object, shall I then just use

mStatic = false;mType = NORMAL_ENEMY;


and if I inherit a Platform

mStatic = true;mType = PLATFORM;


Okey, while I wrote that I realised how logical it was, I really dont need the arguments in the ctor [smile].
Because that is how you would do it, right?

Quote:Original post by rip-off
Also, don't store the object rectangle as a member variable. Instead of calling updateRect() and getRect(), simple make getRect() dynamically generate the rectangle. That, or only store the rectange and drop the x,y,width,height member variables.


I allways felt that it was messy having both x,y,width,height and a rect but didn't know what to do about it. Thanks for telling me!
Well, I would avoid storing the type as an enumeration, as the derived class itself can usually do the work if you design it properly.

For example, instead of:
Object &object = /* ... */if(object.type == FOO){   fooStuff(object);}else if(object.type == BAR){   barStuff(object);}else if(/* ... */){   // ...}

Could be:
class Object{   virtual void foo() = 0;};// Derived classes implement foo() in specific ways.object.foo();

You might find double dispatch a good solution for more complex situations, such as collision detection. It works well when the total number of classes isn't massive. But you generally can reduce the total number of classes with a data driven approach and by moving certain behaviour into auxiliary helper classes.

It depends on what you want to do with the "type" member, but building your own type system that isn't in sync with the regular type system can lead to bugs.
The main reason why I have a type member is for collision detection. On a collision the player should check the mType value of the object and then act accordingly.

I don't understand why you don't think I should store a enum representing the type and setting it in the constructor.

But the mStatic you believe is fine to set in the constructor? I will use the mStatic to find out whether or not to update the object. If it is static I only need to draw it.

then how about adding the object to an update list only if it needs updating? sort of the reverse approach: don't store it in the object, but have lists of objects "of a certain feature".
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

This topic is closed to new replies.

Advertisement