What's your take on protected variables?

Started by
26 comments, last by Codarki 11 years, 5 months ago
The world would be so much easier if there were an extra keyword that is neither private or protected, which would mean that variables are read-only for inherited classes; therefore they can be read (like protected), but can't be modified (like private) without the need for getters. (Edit: And cannot be accessed at all by outsiders, like private & protected)

Whenever I write "getCount()" instead of mCount, it feels to me I'm doing "something" unless I have intelisense and see that getCount() is const. Regardless of seeing the 'get' prefix, the parenthesis just annoys me. It distracts me into thinking my function where I implement my algorithm is also performing other tasks inside it's inherited class instead of just feeding data required by my algorithm.
And what if getCount is not O(1)? I'd have to look at the source code, which cuts my productivity in analyzing functionality that may be totally irrelevant to what I'm writing (and sometimes the src is not available). By reading mCount, I don't have to worry that it's not O(1). If I don't see mCount but see getCount, then I know there might be a damn good reason it's like that. Probably because it can't be cached or the memory overhead is too big (i.e. working with millions of instances)

Of course, in some scenarios may happen that mCount is not up to date, and we have to call updateCount(); therefore getCount starts making more sense. Or if we insist in reading mCount because we know for sure it's up to date (and do it manually if we know it's not), then it means whatever I'm working on is performance intensive (i.e. updateCount is expensive, or may get called too often) and therefore I have to very well aware of the inner workings of the base class.
In this case, the inner working of the base class better be very well documented (which is a good thing IMHO).

Additionally, such keyword would solve the performance issue on compilers that can't inline getters well (including x86/x64 systems when running in debug mode. Don't complain if the build runs 40x slower)

I aknowledge all the reasons about private over protected. But I had to add my two cents because I was feeling that everyone's giving their opinion in favour of private, and seemingly not a single disadvantage or issue against it, as if there were none.

Cheers
Advertisement

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]


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.
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

I believe the context here can't be too obscure. This is a video game dev site after all.


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).


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.


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]

No. As I see it, the Circle class will include all three: width, height and diameter.

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 DRY.

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.
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.

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.

So now you (the programmer) are having justify the inclusion of width and height in your code, and need to band-aid the class to keep width and height in sync with diameter. Why? So you don't have to "bother" adding width and height to the classes that do need it? At the expense of muddying the classes that don't need it?
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?[/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.[/quote]
Well, you might as well repurpose every variable in your code, type-casting ints to bools to chars as you need them, and only have one variable in each function called "data". For the sake of code clarity, each purpose should have it's own distinct variable with a good variable name that describes the purpose. Each variable type should describe the generic use of the data.

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.[/quote]
x_coordinate does not help any more. 'x' is assumed to be a coordinate. The reason I was suggesting it be put in a class/struct, is to force the user of the class to give it a real name, that actually describes its purpose. "topLeftCorner" vs "center" is a completely different purpose, that just happens to share the same generic usage (describing a point in space).

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.[/quote]
But it actually does matter, for code legibility and for the least amount of surprise.

You are saying, every subclass of Shape has x and y, but they can mean different things in each subclass... which means if you use polymorphism, your own code cannot assume that Shape.x means any specific location. That is bad! You're also saying, the end user of the subclass has to look up (for each individual subclass) what x and y means in context to that specific subclass... instead of making the code self-documenting by letting the variable name explain its own purpose.

The only thing each x has in common in each subclass is that it represents a location on a grid. You might as well have each Shape have member variables called "int number", because, hey, each Shape subclass probably might use a number to represent something. rolleyes.gif

The Circle class example is the 'whale' of the mammals class.[/quote]

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?[/quote]
I'm more curious why you are telling me that the user of Dog can't have public access to the dog's Fang.

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?[/quote]
Unless a GermanShepherd has some kind of implementation detail that differs from regular dogs, it should actually be:
Dog myGermanShepherd(...initialization data for the minor differences...);

But if the GermanShepherd *does* have implementation details that differ from regular dogs, a GermanShepherd is-a Dog, so inheritance is correct in this situation. The question is, does a GermanShepherd really need to know the implementation details of its Dog base to function? Every class should mostly be a black box, lest your subclasses become dependent upon implementation details that may later change. Dependency on interface details are fine (and unavoidable), but not implementation details.
You are arguing against inheritance, not against protected variables![/quote]
I am arguing for proper use of inheritance and composition, which gets rid of the need for protected variables. smile.png
Good design eliminates every need for protected variables and functions (at least that I can think of), with only the possible exception of virtual functions... and I'm not even sure if that is truly needed.

Protected variables are fine, if we need them - since good design eliminates the need, why have them if they only enable bad design?

As a beginner programmer, I went from "all members public", to "all members private" (I've also done, "all base class members protected"). Now, I recognize that you can't make hard and fast rules that say "always do X" or "always do Y", so my member variables are more and more becoming public again, unless they are an implementation detail, or unless the implementation needs to do something when setting or getting them. A subclass can easily access the public elements, and use the getters to access the non-public but interface-visible variables.
I don't think a subclass should be able to access anything that a standalone function can't.

I obviously can'y say, "Every use ever of protected variables come from bad design", but I can say, "Every single of my uses of protected variables were a result of my bad design" (or else me doing it "just because"). They probably have some kind of use in butchering the C++ language in weird ways to add new language features, like Boost does.

Boost 1.50 shows 133 uses of of 'protected' in 10,335 classes*, less than 1.5% - so I guess Boost has found some uses for them - they may be misusing them, however, but I'd bet the average Boost developer is a better programmer than I (but again, Boost abuses C++ features to extend the language - so it's not regular code).

*[size=2]869,059 lines of code, not including whitespace.
I think its useful when your intending on creating a derived class that is derived from multiple parent classes.
kind regards,Josh Langley.

I think its useful when your intending on creating a derived class that is derived from multiple parent classes.

Can you elaborate on that?

Does that read as: it's useful to have protected members in parent classes when using multiple inheritance?


IMO if you're in the unfortunate situation that you must use multiple inheritance, don't add more confusion to the mess with protected members.

This topic is closed to new replies.

Advertisement