Sign in to follow this  
Caffeware

What's your take on protected variables?

Recommended Posts

What's your take on protected variables?

In the context of c++, it seems most experts (Scott Meyes, Herb Sutter) think is not a good idea. I don't agree. In fact, some languages, for example Ruby, have only automatic protected variables.
It seems silly to make a 'setter' and 'getter' functions for a variable when the derived object is employ in terms of "is-a". Also, making 'setter' functions opens the variable to be manipulated from outside the object! Of course, one could make in the 'setter' protected, but isn't that 'running in a loop'?

Please, discuss.

EDIT:

Let's say I have:

[source lang="cpp"]
class Shape{
public:
virtual void CalculateArea();
private: //private!
int x, y;
int width, height;
};

class Rectangle : public Shape{
public:
void CalculateArea(){
/* I need width and height to calculate, but can't access it.
What can I do? */
}
};
[/source]
If I make 'getters' for Shape class:

[source lang="cpp"]
class Rectangle : public Shape{
public:
void CalculateArea(){
get_width(); //isn't this silly, as width is a fundamental part of Rectangle
//also, now everyone knows my width!
}
};
[/source] Edited by Caffeware

Share this post


Link to post
Share on other sites
I never use protected in my code. I find that public and private are expressive enough: I either want this to be part of the interface or a detail of implementation, and I don't feel the need to have derived classes have special privileges. On the other hand, I don't use inheritance all that often, so perhaps protected makes more sense in other styles of coding than my own.

Share this post


Link to post
Share on other sites
[quote name='frob' timestamp='1352499274' post='4999455']
Since the compiler optimizers were not that great, having a protected variable meant just a single move instruction whereas mutators and accessors would require multiple jumps and generally hurt performance.
[/quote]

As always, the world has gone full circle :

[url="http://developer.android.com/guide/practices/performance.html"]http://developer.android.com/guide/practices/performance.html[/url]

Google are advocating that programmers should access class field directly, because the Android platform's compiler, at least for now, can't inline acessor/mutator calls.

Share this post


Link to post
Share on other sites
The only time I feel protected members are useful, is with protected functions, as others said (ignoring any performance concerns). Even further, I feel the only functions which should *maybe* be protected, are virtual functions. Any member variable should be private or public. Any non-virtual function should be private or public. Even most virtual functions should be public... but sometimes it's nice to hide them, to not bloat the interface.

A Ctrl+F of the keyword 'protected' in my code base of 209 classes (50k lines of code) finds only two uses of "protected", and both are for a single virtual function in each class (and both are overriding a Qt base-class function. Hmm, odd).

[size=2](It also found a single use of 'goto' =P)[/size] Edited by Servant of the Lord

Share this post


Link to post
Share on other sites
[quote name='Servant of the Lord' timestamp='1352514269' post='4999514'][size=2](It also found a single use of 'goto' =P)[/size][/quote]

Was it "//goto store - buy burritos"? [img]http://public.gamedev.net//public/style_emoticons/default/biggrin.png[/img]

I don't really understand the downside of protected variables. Yeah, you can create a subclass and then access the protected members, but that's only for instances of your derived class.

I mean it just sort of strikes me as the same as the "Do not attempt to stop chainsaw with hands or genitals." sticker.

You can only help people so much. After that they're kind of on their own. If someone wants to use inheritance to abuse a class then they're only hurting themselves, aren't they?

That being said, I usually only use inheritance for polymorphism and use virtual base classes, so maybe I don't know what I'm talking about. Edited by Khatharr

Share this post


Link to post
Share on other sites
[quote name='Khatharr' timestamp='1352519410' post='4999528']
I mean it just sort of strikes me as the same as the "Do not attempt to stop chainsaw with hands or genitals." sticker.
[/quote]

I'm afraid that when it comes to software design a lot of people do still tend to do the equivalent of stopping a chainsaw with their private parts.
Better to educate them about it rather than letting them continue, or let them spread the word to chainsaw-newbies that dismemberment is the right way to go, right?

Share this post


Link to post
Share on other sites
I've answered this question before:

[url="http://programmers.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables/162657#162657"]http://programmers.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables/162657#162657[/url]

Share this post


Link to post
Share on other sites
I know I'm against the fence here, but I'm gonna try anyway .

[quote name='Hodgman' timestamp='1352521684' post='4999533']
The example code is a straw-man argument. It should look like:
[source lang="cpp"]class Shape{
public:
virtual void CalculateArea();
};
class Rectangle : public Shape{
public:
void CalculateArea(){
return width * height;
}
Rectangle( int w, int h ) : width(w), height(h) {}
private:
int width, height;
};[/source]
[/quote]

The thing about this is that for every 'Shape child' I have to include the width, height, x, y variables separately. Isn't one of the advance of class hierarchy not having to repeat oneself.

By the way, I do AGREE that using setter/getters isn't a good idea.

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1352523806' post='4999545']
Can every shape derivative be descrived with those 4 variables? Bregma hinted earlier that this probably isn't true -- e.g. polygons have a collection of vertices and edges, circles have a radius, squares have an edge-length...

If you're making a polymorphic base class, you shouldn't include any variables that aren't going to be common to all derived classes.
If you do have variables that are common to all derived classes, you probably don't need to be using polymorphism either...
[/quote]

First, thanks for the your time and replies; I really appreciated it.

Now, in Bregma's example, I don't see the 'trouble', since the Circle class would include the necessary variables to calculate it's own area. Yet, the Circle class needs the x, y, width and height variables. I believe it's not in-logic to say the a circle has a width, for example.

"In object-oriented programming (OOP), inheritance is a way to reuse code of existing objects..."
[url="http://en.wikipedia.org/wiki/Inheritance_%28object-oriented_programming%29"]http://en.wikipedia.org/wiki/Inheritance_%28object-oriented_programming%29[/url]

I know Wiki is not the best source, but... Edited by Caffeware

Share this post


Link to post
Share on other sites
[quote name='Caffeware' timestamp='1352523153' post='4999541']
By the way, I do AGREE that using setter/getters isn't a good idea.[/quote]
I [i]don't[/i] agree that using setters/getters is a bad idea. [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img] Leastwise, it's not in itself wrong, just over-using it is.

Using setters/getters for every single variable, 'just because', is bad. But using setters where the class needs to know when the member variable changed is important, and using getters when whatever you are getting needs to be calculated, is required.

[quote]The thing about this is that for every 'Shape child' I have to include the width, height, x, y variables separately...[/quote]
Hodgeman rightly mentioned the warning bells that emerge when "Circle" has to ignore either width or height. The warning bells are actually telling you that Circle doesn't have a width or a height, it has a radius or a diameter. You use the diameter to "calculate" the circle's width or height - except that the diameter is exactly equal to the height and is exactly equal to the width, so nobody bothers explicitly calculating it with circles. But just because a circle's width and height happen to be equal to the circle's diameter, that doesn't mean the circle's width and height [i]are[/i] the diameter (or vise-versa). You'd actually be reusing the variable "width" or the variable "height" for an entirely different purpose that just happens to be equal, which wouldn't be good (in this situation, it's a very mild issue - but it's still not a good habit to get into).

There's a more serious, but more subtle issue with re-using the Shape's width, height, x, and y. As I already mentioned, you'd actually be re-purposing either width or height for a different purpose. But how many people here realize you'd also be re-purposing x and y for a different purpose as well? [img]http://public.gamedev.net//public/style_emoticons/default/mellow.png[/img]
What is x and y? A coordinate on a Cartesian grid. Right? But in naming it 'x' and 'y' you are actually naming it what it [u]is[/u], instead of what it's [u]for[/u] (Which is why it would be better off wrapped in a Point or Coord class). A variable's type should say what the variable [u]is[/u], but the variable's name should describe it's [u]purpose[/u].
What does 'x' mean to a Shape? 'x' does not describe the purpose, it only describes that it is a horizontal location in space. It could serve [i]any[/i] purpose - the name isn't describing the purpose.
It's precisely because it's so generically named that you tricked yourself with your Shape class, and also trick future users of your class:
[code]struct Rectangle { int x; int y; };[/code]
[code]struct Circle { int x; int y; };[/code]

What is the difference between the X and Y of the two above classes?
By convention, the x and y of the Rectangle would be the [b][i]upper left corner[/i][/b].
By convention, the x and y of the Circle would be the [b][i]center[/i][/b].

They aren't serving the same purpose! You are repurposing the variables in each subclass to [u]mean something different[/u].
Since x and y really are used to mean a point in space, they should be wrapped together in their own class or struct, and the class' name would then describe the use.
Code using the 'Point' class (like Rectangle or Circle) would then be [i]forced [/i]to come up with a new name, and hopefully, that name will describe the [i]actual [/i]purpose.
[code]struct Rectangle { Point topLeftCorner; };[/code]
[code]struct Circle { Point center; };[/code]
Doing this, you can easily see the difference in purpose between Circle's and Rectangle's use of x and y is as different as 'int health' and 'int money' - all they shared in common was what x and y [i]is[/i], but they don't share in common what x and y are [i]for[/i].

It's a very subtle bit of trickery that is easy to fall into. I encourage separating re-occurring usages of variables like that, out from the classes that use them, and into their own class. I also encourage typedefing variables when they don't actually add anything new but are used frequently in a certain way.
[code]int minutes;[/code]
What is the 'purpose' of the above variable? Nobody has a clue. It's variable name is actually describing what it [i]is[/i] not what it's [i]for[/i].
[code]typedef int Minutes;[/code]
[code]Minutes amountPlayed;[/code]
Ambiguity removed.

The name of a variable makes sense relative to its context. The context is the class or function that contains it. A Circle doesn't have a x or a y which is an int, and Circle [url="http://en.wikipedia.org/wiki/Has-a"]has a[/url] 'center', which [u]is[/u] a Point in space. A Point has a x and a y which is a int, because 'x' as a variable name now has meaning within the context of a Point.

Diameter is not the same as width. Width is not the same as diameter. Even if they are equal and numerically interchangeable, they are not interchangeable purpose-wise.
The x and y of a Circle and a Rectangle is even farther apart purpose-wise (one's purpose is the center, the other's purpose is the top-left corner). Edited by Servant of the Lord

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1352523806' post='4999545']
Regarding the bold bit -- no. You need to investigate the use of composition vs inheritance. Composition is a tool to allow for code re-use (not repeating yourself). Inheritance is a tool to allow for polymorphism. If you're looking for code re-use, you should default to composition, and only use inheritance where necessary.
[/quote]

I would agree on this. The classical orthodox use of inheritance is for specialization. But I think it is common to see inheritance in C++ used (misused?) for extending functionality of a base class, which is really a way of re-use, not polymorphism.

In the Google Go programming language, they went so far as to not support inheritance. So you can only use composition if you want to "inherit" implementation. Polymorphism is solved by implicit fulfillment of an interface, which is a separate declaration. At first, you feel handicapped. But after a while you realize your don't miss the inheritance hierarchies.

Share this post


Link to post
Share on other sites
[quote name='Caffeware' timestamp='1352496482' post='4999429']
What's your take on protected variables?

In the context of c++, it seems most experts (Scott Meyes, Herb Sutter) think is not a good idea. I don't agree. In fact, some languages, for example Ruby, have only automatic protected variables.
It seems silly to make a 'setter' and 'getter' functions for a variable when the derived object is employ in terms of "is-a". Also, making 'setter' functions opens the variable to be manipulated from outside the object! Of course, one could make in the 'setter' protected, but isn't that 'running in a loop'?

Please, discuss.

EDIT:

Let's say I have:

[source lang="cpp"]
class Shape{
public:
virtual void CalculateArea();
private: //private!
int x, y;
int width, height;
};

class Rectangle : public Shape{
public:
void CalculateArea(){
/* I need width and height to calculate, but can't access it.
What can I do? */
}
};
[/source]
If I make 'getters' for Shape class:

[source lang="cpp"]
class Rectangle : public Shape{
public:
void CalculateArea(){
get_width(); //isn't this silly, as width is a fundamental part of Rectangle
//also, now everyone knows my width!
}
};
[/source]
[/quote]

the problems with getters and setters is, people take them too literally.

they make a get and set for each variable, allowing users to, again, invalidate state.

your rectangle class needs left, right, width, top, bottom, height, area, aspectratio, etc to be publically accessible. and none of those getters/setters/whatevers should make it possible to have an invalid rectangle, ever.

set_width() can check if you enter a negative width, and prevent it. set_right can check if your right is more left than your left, and prevent it. and vice versa.

it's not about making methods that directly expose your variables in public. if you want that, use public directly. getters and setters is about NOT exposing anyting directly. aobut making SMART getters and setters. in that case, about making a rectangle ALWAYS a rectangle. not something that has negative space, or what ever.

and as having an object valid at all time is very important, public variables are dangerous. as are protected ones.

but forget about getters and setters. don't write a car that has get_position and set_position. write a car that can drive(), turn(), break() and return current_position(), current_orientation() instead.

if you expose your variables directly, you did not understand the idea of getters and setters.

Share this post


Link to post
Share on other sites
I believe the context here can't be too obscure. This is a video game dev site after all.

[quote name='Servant of the Lord' timestamp='1352536318' post='4999582']
Hodgeman rightly mentioned the warning bells that emerge when "Circle" has to ignore either width or height. The warning bells are actually telling you that Circle doesn't have a width or a height, it has a radius or a diameter. You use the diameter to "calculate" the circle's width or height - except that the diameter is exactly equal to the height and is exactly equal to the width, so nobody bothers explicitly calculating it with circles. But just because a circle's width and height happen to be equal to the circle's diameter, that doesn't mean the circle's width and height are the diameter (or vise-versa). You'd actually be reusing the variable "width" or the variable "height" for an entirely different purpose that just happens to be equal, which wouldn't be good (in this situation, it's a very mild issue - but it's still not a good habit to get into).
[/quote]

No. As I see it, the Circle class will include all three: width, height and diameter. The width and height still serves the same purpose for Circle class as it did for Rectangle class. Now, the diameter variable (or maybe radius), will be used to calculate the area of the circle.

[quote]
There's a more serious, but more subtle issue with re-using the Shape's width, height, x, and y. As I already mentioned, you'd actually be re-purposing either width or height for a different purpose. But how many people here realize you'd also be re-purposing x and y for a different purpose as well?
...
What is x and y? A coordinate on a Cartesian grid. Right? But in naming it 'x' and 'y' you are actually naming it what it is, instead of what it's for (Which is why it would be better off wrapped in a Point or Coord class). A variable's type should say what the variable is, but the variable's name should describe it's purpose.
What does 'x' mean to a Shape? 'x' does not describe the purpose, it only describes that it is a horizontal location in space. It could serve any purpose - the name isn't describing the purpose.
It's precisely because it's so generically named that you tricked yourself with your Shape class, and also trick future users of your class:
[/quote]

I don't agree with this. The width and height variables are raw data. Data should be used for whatever it needs to be used.
Of course, the 'x' variable could be named better, let's say 'x_coordinate', or even better, as you say, it could be a point. But my argument is that EVERY child of the Shape class will need a x variable and a y variable, or a point, etc.
It doesn't matter where the point will be located in the shape, the object stills need it.

The Circle class example is the 'whale' of the mammals class.

I must say, I have nothing against composition. I use it much in my code. Yet some things simply belong in a class. And I can't conceive how the child can't have access to the parents data when he is INHERING from that same class. If you permit to me make another example:

[source lang="cpp"]class Dog{
private:
Fangs fangs;
//many other variables
};
class GermanShepherd : public Dog{}[/source]

So you are telling me that class GermanShepherd can't have access to Dog class's private variables?

Or are you going to argue that some dogs don't howl, so they don't fit together in a class. Should I compose GermanShepherd class from Dog class?

You are arguing against inheritance, not against protected variables!

[font=courier new,courier,monospace]inhere [?n?h??]
vb (intr; foll by in) to be an inseparable part (of)[/font] Edited by Caffeware

Share this post


Link to post
Share on other sites
[quote name='Caffeware' timestamp='1352654682' post='4999956']
No. As I see it, the Circle class will include all three: width, height and diameter.[/quote]
Why? Width and height can be calculated from diameter (or radius). Including all three just wastes memory and gives you more opportunities to let data get out of sync. See [url=http://en.wikipedia.org/wiki/Don't_repeat_yourself]DRY[/url].

[quote]The width and height still serves the same purpose for Circle class as it did for Rectangle class. Now, the diameter variable (or maybe radius), will be used to calculate the area of the circle.[/quote]
These two sentences contradict each other. Width and height for the rectangle are used to calculate area. Diameter or radius are used to calculate area for the circle. Width and height obviously do not serve the same purpose for a circle. If you're saying clients of the shape base class can use the height and width of a circle the same way they could use the height and width of a rectangle you've just described an interface: i.e. virtual functions the base class can supply, not what should be member variables of the base class.

But again, you keep supplying toy examples without context. Real examples with actual context include how your class hierarchy is intended to be used, not just what it's intended to be modeled. How do you plan to use your Shape class or your Dog class? What problem are these meant to solve? Why should your Dog class even have fangs in the first place? Without context it's impossible to say whether x, y, height, width or fangs are even necessary much less why or why not derived classes should have access to them.

Share this post


Link to post
Share on other sites
I find I use protected variables from time to time. Most of the time they are const variables initialized once, so there's no need to worry about invalid state, or messing things up, ect... In theory they could be made public but why clutter the public interface? The other time is for small/specific classes that I have no intention of anyone else inheriting from, and then its mostly for convenience. Off the top of my head I can't think of any time I've absolutely required them, but none-the-less I'm quite glad they're there.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this