Archived

This topic is now archived and is closed to further replies.

Accessor methods in C++ classes

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

I was wondering if I should create accessor methods for all my private/protected variables and use those instead of variables directly. I really don''t know how far should I get with these. Eventually I would make the accessors inline, so there would be no speed penality, but I am still confused on how often and where to use accessor methods. Maybe I should only create accessor methods for the variables I want to give public access to the class user? Please clarify this mess for me Damjan "Rhuantavan" Mozetic http://users.volja.net/mozeticd/

Share this post


Link to post
Share on other sites
Well, if you want to be purely OO then you make all of your variables private and provide protected and public accessor methods for both reading and writing for those variables that you want exposed to the outside world.

i see that as a bit overkill though, i mostly use accessors for variables i want read/write access inside the class and read-only access outside the class. i.e. i make the variable private and provide an accessor that returns a const reference.

Share this post


Link to post
Share on other sites
by providing accessors for everything you only want available as read-only, it makes sure that the class is used the way it was intented, making it a better design than something that allows dodgy hacks of variables ''internal'' to the class. so for that reason i''d say it definitely isn''t overkill.

as for providing accessors for everything....it really depends on how much you think the internal structure of the class could change. Take a Rect class for example


  
class Rect {
float t,l,b,r;
public:
float GetTop(void) { return t; }
float GetLeft(void) { return l; }
float GetBottom(void) { return b; }
float GetRight(void) { return r; }
float GetWidth(void) { return r-l; }
float GetHeight(void) { return b-t; }
// equivilent Set* members....

};


now say you''ve got a whole bunch of code that uses this, and for whatever reason you want to change the Rect class to be stored using width&height instead. no problems, change your variables to float x,y,w,h and your accessor functions. no changes are needed to code outside of the class. if you were accessing variables directly though.....well, that would be one pain in the ass

and before ne1 says it....yes a rectangle is a crappy example, but it demonstrates the idea, and it''s the first thing that came to mind

so if the class is used a lot and you''re expecting internal changes in the future, go for it...give all variables you want public/protected accessors. but if not, why bother?

Share this post


Link to post
Share on other sites
You'r right, thanks

And another thing. How about decorating private vabiables with a leading underscore? Bruce Eckel in his Thinking in C++ book says you should not use leading underscores because they are reserved. Still I've seen some people use it. I think it really gives a clear picture of what's private and what's not.

[edited by - Rhuantavan on June 23, 2002 7:05:07 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by Rhuantavan
You'r right, thanks

And another thing. How about decorating private vabiables with a leading underscore? Bruce Eckel in his Thinking in C++ book says you should not use leading underscores because they are reserved. Still I've seen some people use it. I think it really gives a clear picture of what's private and what's not.

[edited by - Rhuantavan on June 23, 2002 7:05:07 AM]


Bruce Eckel is correct. Leading underscores are reserved for compiler writers and it is a bad idea to start any identifier with an underscore. An alternative that doesn't require too much more typing is the Hungarian notation method of prefixing member variables with 'm_'. I find that having a variable with a name like m_width makes it stand out quite obviously as a member variable. Along those same lines, Hungarian notation recommends g_ for globals instead of some other convention like all capitals.

[edited by - jaxson on June 23, 2002 11:25:34 PM]

Share this post


Link to post
Share on other sites
The most pressing reason to use accessors, at least IMO for what it's worth, is syntaxic uniformity. It's easier to use (template-based) functional adaptors with methods than with properies. For this reason, if you do make a property public, it's not a bad idea to add accessors for those properties as well. It would be easier to use std:air with conditional algorithms (i.e. searches) if there were two methods called first() & second() that returned references to the underlying data elements.

The most pressing reason not to use accessors is because it's a waste of time. It abstracts nothing, and therefore accomplishs no work. If it _does_ abstract something, it shouldn't be an accessor, it ought to be a 'normal' method with a more descriptive name.



...
An alternative to HN, is to use language constructs to signify the scope of the variables and rely on the compiler for type checking. It requires a little more due-diligence, but after trying it I like this method better.


CClass::Method()
{
//Explicit use of this-> clearly indicates it's a property
//Same goes for methods
this->property = 0;

//Scope accessor :: clearly indicates global scope
::global_hole

}

No decoration would indicate a local. I decided that distinguishing between 'true' locals & function parameters is more bother than it's worth.


...
Technically I think you're allowed to use a preceeding underscore on properties (and variables inside of a namespace), so long as they do not start with a capitol letter (I think, ...might have that backwards [edit, I had it backwards, fixed]).

[edited by - Magmai Kai Holmlor on June 26, 2002 1:33:43 AM]

Share this post


Link to post
Share on other sites
I am reluctant to the use of hungarian notation... maybe because it reminds me of microsoft

I use the scope resolution operator :: for globals, but I''ve been using the leading underscore for my private and protected variables and until now had no problems. I''ve seen the underscore usage in Gamma, Helm, Johnson & Vlisside''s Design Patterns book with the samples in C++, so I thought their usage appropriate for myself.

Concerning accessors, I think I''ll use them all over. Another thing. Do you use the accessors to get private variables even inside private methods, or do you refer to the variables directly when going "deeper" into the implementation of the classes? Am I complicated? Sometimes I think I am...

Share this post


Link to post
Share on other sites
Read the excellent CUJ article[1] by Herb Sutter and Jim Hyslop on the subject.
quote:

It abstracts nothing, and therefore accomplishs no work.


It abstracts the internal representation of that data in the class, which is the whole point. You too would benefit from that article.

[1]http://www.cuj.com/experts/1902/hyslop.htm?topic=experts

Share this post


Link to post
Share on other sites
Guys, can you please point me to such discussions and/or articles as I am unable to find them.

Last I've read, people find get/set methods a bad design issue. However I think they meant that for public methods. How about making PRIVATE accessors/mutators for private variables?

Thank you!

[edited by - Rhuantavan on June 25, 2002 7:46:59 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by Rhuantavan
Guys, can you please point me to such discussions and/or articles as I am unable to find them.



Here is a previous debate on the subject.

Also, along with Arild Fines link here''s another article by Herb Sutter on encapsulation which I particularly like.

quote:
Original post by Rhuantavan
Last I''ve read, people find get/set methods a bad design issue.



News to me. There are some people who dislike accessors, but I''ve never heard of the get/set naming convention being considered a bad idea (although I''m sure there are a few people who dislike it)...

quote:
Original post by Rhuantavan
How about making PRIVATE accessors/mutators for private variables?



I see no reason to hide private class data from the class itself. If there is extra work that must be done each time a variable is accessed (whether internally or externally) then by all means use a private accessor, otherwise you may as well access the variable directly.


- Houdini

Share this post


Link to post
Share on other sites
quote:
Original post by Houdini
Also, along with Arild Fines link here''s another article by Herb Sutter on encapsulation which I particularly like.



There''s at least one instance of a serious boo-boo in that article:

I quote:
quote:
Herb Sutter
Point 3[Note: that replacing protected members by private members with protected accessors is without cost] trivial to show. The inline function, which returns by reference and hence incurs no copying cost, will probably be optimized away entirely by the compiler.



This is supposed to be a software engineer, and he has the audacity to say "probably be optimized"??? Hey, I''ll try that in my next meeting. "Yeah, this solution will PROBABLY work."








People might not remember what you said, or what you did, but they will always remember how you made them feel.

Share this post


Link to post
Share on other sites
quote:
Original post by Magmai Kai Holmlor
Get/Set accessors fail to abstract the internal representation - at best they mis-represent it.

What do you mean "mis-represent"? The accessors are part of the public interface - they are the representation. The internal representation is of no concern to the outside world.


Share this post


Link to post
Share on other sites
quote:
Original post by Houdini
[quote]Original post by MadKeithV
This is supposed to be a software engineer, and he has the audacity to say "probably be optimized"??? Hey, I''ll try that in my next meeting. "Yeah, this solution will PROBABLY work."


Well, he can''t gaurantee that joe-schmoe won''t release some half-baked compiler that doesn''t inline accessor functions. But if you''re using a compiler like that then you probably have more important things to worry about than non-inlined accessor functions .


quote:
Original post by Arild Fines
What do you mean "mis-represent"? The accessors are part of the public interface - they are the representation. The internal representation is of no concern to the outside world.


I believe, and correct me if I''m wrong Magmai, that he''s referring to the fact that with public variables you know that it''s quick to access them.

However, accessor functions could be doing some intensive work behind the scenes which would cause a bottleneck in performance if the user retrieves values constantly. The user could have just called the accessor function once and saved the value and referred to that cached value had they known what the accessor function was doing, but they didn''t.

Although I see his point (if that even is his point ) the same could be said about accessors over public variables.

If you only use public variables then those variables must contain the proper values at all times. If computation must be performed to supply those variable with values then that could waste quite a bit of processing power. Using accessors you could do some lazy computation and only compute (then cache) the values that the user tries to access, and not worry about the ones they don''t access, thus potentially making your class much faster.

Besides, the user should never make assumptions on what a class function does. That''s the whole point of encapsulation, that the internals can change whenever. If there are potential performance problems with a few functions then those should be documented so the user knows what to avoid.

Those articles also state other advantages accessors have over public variables, but I''m not going to get into another debate with Magmai on this. I think we''ve both agreed to disagree on this subject


- Houdini

Share this post


Link to post
Share on other sites
I'd like to read Bjarne Stroustrup's back-tracking on protected member variables, it should be a riveting read!

I'm actually trying to follow those guidelines in a small project right now, to see if I run into any problems other than "grr, that would have been SO much faster to type if I could just access that member of my parent class..."

Any compiler worth its salt should indeed optimise that accessor out completely, IF it is written that way. I was originally going to object, saying "but you're not guaranteed it's written that way!", but really, that should be part of the design contract. If this is supposed to be a performance-critical class, you better not have oodles of run-time checking in there when I'm expecting it to act like a replacement for a direct-access member variable!

Another positive thing about it:
if you REALLY need to change the underlying representation, and the accessor REALLY needs to change, your code will break, and will need to be fixed. This will annoy people, but it's a Good Thing(tm) because, NEXT time you design a class, you'll make sure your design is right the first time around


People might not remember what you said, or what you did, but they will always remember how you made them feel.

[edited by - MadKeithV on June 25, 2002 3:14:40 PM]

Share this post


Link to post
Share on other sites
To clarify on the issue of leading underscores...

According to the C++ Standard, 17.4.3.1.2 Global Names [lib.global.names], paragraph 1:

Certain sets of names and function signatures are always reserved to the implementation:

Each name that contains a double underscore (__) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implemenation for any use.
Each name that begins with an underscore is reserved to the implementaiton for use as a name in the global namespace.

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
quote:
Original post by Houdini
[quote]Original post by Arild Fines
What do you mean "mis-represent"? The accessors are part of the public interface - they are the representation. The internal representation is of no concern to the outside world.


I believe, and correct me if I''m wrong Magmai, that he''s referring to the fact that with public variables you know that it''s quick to access them.
Yes, side-effects are evil. The unfortunately reality is, implementations matter because behavior matters.
And don''t get me started about get/set tuples again - I like & use accessors. My issue isn''t that accessors are bad, but that I often find the reasons for their promotion dubious.

quote:

If you only use public variables then those variables must contain the proper values at all times.


Not so - some computation or initialization could be required prior to asserting the validity of the state of the public holes. The advantage here, is that such computation or initialization can be invoked only when necessary - the disadvantage is that neglecting to initialize the state engenders undefined behavior. Often computations will result in other useful pieces of information as by-products. With a state-less interface, you either need to recompute on-demand, or internally maintain state information. The first case is obviously undesirable, and the second option is problematic. An alternative is to inform the parent object of the relevant details, and let them manage you - not the other way around.

quote:

Using accessors you could do some lazy computation and only compute (then cache) the values that the user tries to access, and not worry about the ones they don''t access, thus potentially making your class much faster.


Hum, I''ll tap Modern C++, and counter your ''lazy evaluation optimization'' elussion to Meyers, Effective C++ #20 with Alexandrescu, "Optimization that aren''t: In a Multi-Threaded World"

quote:

Besides, the user should never make assumptions on what a class function does. That''s the whole point of encapsulation, that the internals can change whenever. If there are potential performance problems with a few functions then those should be documented so the user knows what to avoid.


That''s SOOO 1990''s
Seriously, (and I imagine you''re well aware that) more recent OO involves contract based relationships, in which not only the interface is unmutable, but the behavior is not allowed to change either.
I agree that encapsulation is important - but what we encapsulating? The data or the behavior?
And think about it, what''s ultimately which is more important? If we could/will/already do automate interface changes (say you decide get_inertia_tensor() is stupid, and globally search & destroy it with inertia_tensor()), all is well - change the behavior and your asking for trouble. With apt generic finesse, you can even transparently change the return type of inertia_tensor – a significant interface change, yet you can assert consistent behavior. Use a nested-typedef, inertia_t, which can be defined as either a scaler for 2D problems, or a 3x3 matrix for 3D problems. In both cases t = I a .

Interface abstraction of mutable behavior is a pipedream (or a mistake). The ideal interface abstracts precisely to the invariant behavior.

If your behavior changes, you want the code to break . This prevents you from running until you address every instance affected by the change in behavior.

Software in built upon assumptions, if you can’t make any assumptions, you can’t abstract anything.
Suppose we start making a scene graph, and start with a behavioral requirement that all nodes shall have a non-mutable bounding sphere. Later, we realize this is stupid, and lose the non-mutable requirement. It''s a simple change to the node class - but then we have code that has been built assuming the bounding sphere is non-mutable. Code built on wrong assumptions is never a good thing.

The problem is this; you must make assumptions to write software, and your software is only as valid as all of it’s assumptions. If any of it relies on an invalid assumption, then it doesn’t run correctly (or, if you’re lucky, it doesn’t even compile). If a component makes a questionable (or invalid) assumption, then everything based upon that component also makes the same questionable (or invalid) assumption.
That’s why software changes are so expensive. If a change invalidates a prior assumption, all code base upon any component that made that assumption is also invalidated.

Share this post


Link to post
Share on other sites
quote:
Original post by Magmai Kai Holmlor
Not so - some computation or initialization could be required prior to asserting the validity of the state of the public holes. The advantage here, is that such computation or initialization can be invoked only when necessary - the disadvantage is that neglecting to initialize the state engenders undefined behavior.


Wow, we are complete opposites in our programming direction. I'm going for generic self-contained modules that can be re-used/replaced at will. I believe that each class/module should be required to take care of itself, and only itself. The advantages of this over exposing implementation details, IMO, are:

1) Flexibility - Classes/modules can be completely replaced with a whole new implementation without breaking code. For example, I write an image library that, late in development, I realize is buggy/slow or I just don't have time to complete. Since everything is encapsulated in the class I can just write a wrapper class for FreeImage or DevIL, replace my classes with the new implementation, and my program compiles perfectly without and changes being made to code that uses the image library.

2) Less bugs - Since all implementation details are encapsulated the user no longer needs to worry about forgetting to call internal functions.

3) Faster/easier development - Not having to remember all that extra code, plus typing all that extra code, plus fixing bugs that extra code creates means faster development or more time in making other parts of your application better.

4) Lower dependancies - If a user is required to call functions specific to an implementation, then their code is now dependant on that implementation. This means longer compile times and less flexibility. For instance, using a generic wrapper classes with full encapsulation means I could use the Observer and Factory patterns combined with library files (.DLL or .SO) to create a plugin system where I could replace my graphics, sound, and input modules at runtime. If my application was required to know about implementation details then a plugin system would in impossible.

quote:
Original post by Magmai Kai Holmlor
Hum, I'll tap Modern C++, and counter your 'lazy evaluation optimization' elussion to Meyers, Effective C++ #20 with Alexandrescu, "Optimization that aren't: In a Multi-Threaded World"


I don't write multi-threaded code unless absolutely necessary as I believe it's problematic at best (ie, more of a pain than it's worth). Taking a look at how many complications arise when trying to do something as simple as sharing a string between two threads only makes my stance more firm... But that's not the issue.

Yes, I'm forced to agree with the problems and solutions stated in that article. However, threading is a very unique problem that requires a unique solutions. Just because you'd want to lock the string outside the string class doesn't mean you should handle all details outside the string class.

Would you require your users to check if the internal string buffer needs to be resized before setting the string value, or would your string class automatically handle that behind the scenes? Isn't that part of the implementation? So what parts do you require the user to call manually and what parts do you handle internally?

BTW, had I written a string class with "copy on write" optimization and I wanted to use it in a multi-threaded environment all I'd have to do is change the implementation of the class. As far as users of the class is concerned nothing's changed. This is the whole reason for encapsulation in the first place.

BTW, I think this is the perfect example of why encapsulation is a Good Thing. As you state, assumptions need to be made. If I know my program will be single-threaded with lots of string copies I can add the "copy on write" optimization. If I then need to use my string class in a multi-threaded environment I could change the implementation to remove that optimization wihout changing the interface. This is the whole reason for encapsulation in the first place.

quote:
Original post by Magmai Kai Holmlor
If your behavior changes, you want the code to break . This prevents you from running until you address every instance affected by the change in behavior.

Software in built upon assumptions, if you can’t make any assumptions, you can’t abstract anything.
Suppose we start making a scene graph, and start with a behavioral requirement that all nodes shall have a non-mutable bounding sphere. Later, we realize this is stupid, and lose the non-mutable requirement. It's a simple change to the node class - but then we have code that has been built assuming the bounding sphere is non-mutable. Code built on wrong assumptions is never a good thing.



Yes, if your behavior changes you want your code to break. But you don't want it to break if your implementation changes. In other words, ending up with the same result is all that matters. It doesn't matter how you get there, just as long as you get there.

So changing the node class from non-mutable to mutable changes the behavior, so you are right, you do want code that uses that class to break. But if I changed the way internal data types are stored in the class I sure don't want all the code that uses that class to break.

quote:
Original post by Houdini
but I'm not going to get into another debate with Magmai on this.



Damnit Magmai, see what you've done??!!


- Houdini


Update: BTW, I've actually not heard of "contract based relationships" in OOP. I wouldn't mind reading up on something like this, to get a better idea on where they/you are coming from. Any links?


[edited by - Houdini on June 26, 2002 10:29:40 AM]

Share this post


Link to post
Share on other sites
COM is an example of contract based OO. i.e. What IPersist is supposed to do, will never change, and if you implement IPersist, you're not suppose to do anything more than what IPersist requires.

quote:

Do you use the accessors to get private variables even inside private methods, or do you refer to the variables directly when going "deeper" into the implementation of the classes?


I think that would qualify as neurotic (class) behavior. You can get yourself into a situation where you need to use the public accessors if they invoke requesite side-effects (e.g. lazy evaluation). If the class that the data belongs too isn't even suppose to directly access it, maybe it shouldn't be part of that class, but another one?

In debug builds, accessors do have overhead - so if you use them on a very low-level in complex algorithms, you can impact the performance to the extent that you are unable to effectively run the program in a debug build (say you used them inside of an FFT algorithm and then called it continuously). Usually it's not a problem.

[edited by - Magmai Kai Holmlor on June 26, 2002 11:50:03 PM]

Share this post


Link to post
Share on other sites
I use accessors but I also allow direct access. I''m leaning towards accessors though. One might do checks in the accessor function before returning the data to client. I do allow direct access to STL containers and other inner objects. I''ve also tried a new naming convention that I like especially using vc++6 ide in class view. I prepend my worker functions(non-public) with ''z_'' and structs with ''A_'' this way workers stay at the bottom on function list and structs stay at the top of all ''C'' prepended class names in class view. I have a clearer view of things this way. Global object data is ''m_'' prepended. It''s also clearer to me seeing I''m calling my workers rather than some win32/mfc code.

Share this post


Link to post
Share on other sites