 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Zap Guest
|
Posted: Fri Dec 17, 2004 1:39 pm Post subject: Accessors vs public data members |
|
|
Widespread opinion is that public data members are evil, because if you
have to change the way the data is stored in your class you have to
break the code accessing it, etc.
After reading this (also copied below for easier reference):
http://groups.google.it/groups?hl=en&lr=&safe=off&selm=6beiuk%24cje%40netlab.cs.rpi.edu&rnum=95
I don't agree anymore.
Code that uses x = obj.data_get() and obj.data_set(x) is uglier, longer
to write and more difficult to read than x=obj.data and obj.data=x .
Furthermore, chances that in future the implementation will beed to be
changed are in general rather small: I would say less than 10%: of 10
classes with data elements, likely less than 1 will really need to be
modified in the future by putting code in the place of raw data members.
To avoid problems with that only class over 10, the current widespread
practice is to use getters and setters also to access members of the
other 9 classes.
Jerry Coffin's approach seems to me much better than getters and
setters. However I'm trying to analyze the drawbacks of Coffin's
approach. Correct me if I am wrong:
- The compiler is forced to leave at least 1 byte for the nested class
X, while would need zero for getters and setters
- Inheriting the class "whatever" won't let you change the getters and
setters. However: 1) with the getters and setters you anyway needed to
foresee need for inheritance and declare them virtual (*), which usually
nobody does 2) if you foresee need for inheritance, you can do that also
with Coffin's tecnhique, by putting a reference to X inside "whatever"
instead of an instance of X. You initialize the reference in the
constructor. In the derived class you initialize it pointing to an XDerived.
- The standard allows only 1 implicit conversion to be performed. So if
the accessing code uses X (while reading), the 1 implicit conversion (to
int, in the example) is already used. If the old accessor code is
relying on another implicit conversion to be performed (but I see this
as rather unlikely), that code will break.
These three drawbacks IMHO don't seem to compensate for the hassle of
using accessors for all the classes which might, in the future, need
code instead of data. However I'm waiting for comments...
Thanks for your comments :-)
Zap
(*) Ok... sometimes inheritance is useful even without "virtual"
-----------------------------
[Jerry Coffin post copied for reference]
From: Jerry Coffin (jco--- (AT) ta---- (DOT) com)
Subject: Re: Member name style
View: Complete Thread (119 articles)
Original Format
Newsgroups: comp.lang.c++.moderated
Date: 1998/02/06
In article <6bd2u9$44l (AT) netlab (DOT) cs.rpi.edu>, [email]lu---- (AT) us (DOT) i--.com[/email] says...
[ direct access to variables vs. get/set methods ]
| Quote: | But what if in the future you change the attribute to be calculated or
obtained from some database? Then, all the dozens or hundreds of
places that directly access the member variable will have to be
modified to use the getter method. Using getter methods all the
time, hides the internal implementation and let's you change that
internal implementation with no ripple effects to the rest of the
code.
|
When/if you really run into this, you can avoid the problem quite
easily. Instead of using a member function, and converting all access
to the variable to use the member function instead, you simply turn
the member variable into a class of its own. Provide that class with
conversion operators to the original type, and voila' your code gets
executed as necessary. E.g.:
class whatever {
public:
int x;
// ...
};
turns into:
class X {
public:
operator int() {
// access database or whatever to determine value
return value;
}
X operator=(int new_value) {
// set value in database, or whatever...
}
};
class whatever {
public:
X x;
// ...
};
Now assignments to and from x use your code anyway, but avoid having
to make it clear in all other code that you're using code rather than
simply accessing a variable.
Keep in mind that a single class should generally be responsible for
only a single thing. If use of a particular member variable starts to
become more complex than simply assigning to it/using its value, it
most likely belongs in a class of its own.
I'm not sure I'd particularly recommend this technique for new code,
but if you have some existing code that allows public access to its
variables, it can make long-term maintenance much easier.
I should add that problems can arise with this basic design as well.
In particular, if you can no longer reasonably represent the TYPE of
thing being dealt with as the original type at all, then you have to
make more widespread changes. If there's ANY chance of this happening
at all, you're probably best off to start with the return being a
class or at least a typedef rather than making it a built-in type.
This will generally ease any long term transitions you have to make.
--
Later,
Jerry.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
glenlow@pixelglow.com Guest
|
Posted: Fri Dec 17, 2004 3:10 pm Post subject: Re: Accessors vs public data members |
|
|
| Quote: | - The compiler is forced to leave at least 1 byte for the nested
class
X, while would need zero for getters and setters
|
This is illusory since in Coffin's implementation the int is stored in
the X object, so there shouldn't be any overheads.
| Quote: | - Inheriting the class "whatever" won't let you change the getters
and
setters. However: 1) with the getters and setters you anyway needed
to
foresee need for inheritance and declare them virtual (*), which
usually
nobody does 2) if you foresee need for inheritance, you can do that
also
with Coffin's tecnhique, by putting a reference to X inside
"whatever"
instead of an instance of X. You initialize the reference in the
constructor. In the derived class you initialize it pointing to an
XDerived. |
You can adopt a compromise: declare your get and set as private, then
arrange X to call your get and set methods. This way you can easily
override your get and set in a subclass. If you use templates
aggressively it's possible to keep the overheads of the "property"
class to a minimum and not have to do busywork for each new property.
Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dave Harris Guest
|
Posted: Sun Dec 19, 2004 1:52 am Post subject: Re: Accessors vs public data members |
|
|
[email]Zap (AT) zup (DOT) com[/email] (Zap) wrote (abridged):
| Quote: | Widespread opinion is that public data members are evil, because if you
have to change the way the data is stored in your class you have to
break the code accessing it, etc.
|
And also because it makes it harder for the class to maintain its class
invariants involving that variable, if there are any.
And also for consistency. To have some variables accessed directly and
some via a function makes an unnecessary learning burden for clients.
Abstraction isn't there just in case the details change. It's also there
to simplify; to reduce what clients need to know.
And also because it falls out of the design process. Usually you should
design the class's interface first, with get/set methods as needed, and
then later design the implementation, which may include variables which
need only one, or even neither, of the two functions, or for which the
internal representation is different to the interface.
| Quote: | Code that uses x = obj.data_get() and obj.data_set(x) is uglier, longer
to write and more difficult to read than x=obj.data and obj.data=x .
|
That's partly a matter of naming conventions. x = obj.name() isn't too
bad, and in fact no longer than x = obj.m_name (and I do like instance
variables to be marked somehow, even if they are public).
For the set function, I think the longer name is worth while. This is
because changing the state of an object is a big deal, and worth
highlighting in the code. I find it easier to grep for set_name than for
assignments (especially as clients can take the address of the public
variable and assign through an alias). It is easier to set debugger
breakpoints if all changes go through one function.
(Incidently, I hate the conventions which use overloading, like
obj.name(x). In my eyes, getting and setting are so different they deserve
different names.)
| Quote: | Jerry Coffin's approach seems to me much better than getters and
setters. However I'm trying to analyze the drawbacks of Coffin's
approach.
|
You missed the point about class invariants. For example, suppose the
class has a cache which must be updated whenever a public member variable
changes. In general you need to give the wrapper class a reference to the
enclosing class so that it can do the necessary updating. That's some
extra memory overhead, not to mention a much more complex design.
| Quote: | However: 1) with the getters and setters you anyway needed to
foresee need for inheritance and declare them virtual (*), which
usually nobody does
|
That's partly because if you add virtual it only takes a recompile to fix
up clients. It's automated, with almost no work and almost no chance of
human error. Changing every obj.name = x to obj.set_name(x) requires
checking all the client code out of version control and editing by hand.
| Quote: | if you foresee need for inheritance, you can do that also with
Coffin's tecnhique, by putting a reference to X inside "whatever"
instead of an instance of X. You initialize the reference in the
constructor. In the derived class you initialize it pointing to an
XDerived.
|
That assumes you have something you can initialise the reference to. One
reason for introducing subclasses is so that they can be simpler, take
less space and be more efficient. It's harder to do that if you have
publically committed to having the instance variables in the base.
-- Dave Harris, Nottingham, UK
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Allan W Guest
|
Posted: Wed Dec 22, 2004 2:39 am Post subject: Re: Accessors vs public data members |
|
|
Zap wrote:
| Quote: | Widespread opinion is that public data members are evil, because if
you
have to change the way the data is stored in your class you have to
break the code accessing it, etc.
|
Those aren't the only reasons, as has been pointed out by others.
These accessors aren't any more OO than public members, BTW.
Some good advice is to never have any code that relies on knowing
ANY implementation details of an object. Instead of a public member
named address, or accessors
.. std::string Customer::get_address();
and
.. void Customer::set_address(std::string);
you should instead have action-oriented methods:
.. void Customer::get_new_address(Window &window);
.. void Customer::show_name_and_address(Printer &printer);
.. void Customer::save_to_database(Database &database);
....etc.
| Quote: | Furthermore, chances that in future the implementation will beed to
be
changed are in general rather small: I would say less than 10%: of 10
classes with data elements, likely less than 1 will really need to be
modified in the future by putting code in the place of raw data
members. |
How did you arrive at these figures? How much code did you study, and
how
long did you study the changes?
A better argument would be: With some foresight, you can usually see
which
members are likely to change implementation in the future.
But there are still drawbacks, not the least of which is: even for
experts, foresight isn't perfect. (No, I don't claim to be an expert.)
| Quote: | Jerry Coffin's approach
|
is called a proxy.
| Quote: | seems to me much better than getters and
setters. However I'm trying to analyze the drawbacks of Coffin's
approach. Correct me if I am wrong:
- The compiler is forced to leave at least 1 byte for the nested
class
X, while would need zero for getters and setters
|
Not neccesarily. If there aren't any virtual functions or padding
bytes,
the size of X is probably the same as the data it contains.
| Quote: | - Inheriting the class "whatever" won't let you change the getters
and
setters.
|
Making the member public won't let you do this either.
| Quote: | However: 1) with the getters and setters you anyway needed to
foresee need for inheritance and declare them virtual (*), which
usually
nobody does
|
Why would they need to be virtual? Either the base overrides them, or
it
doesn't.
| Quote: | 2) if you foresee need for inheritance, you can do that also
with Coffin's tecnhique, by putting a reference to X inside
"whatever"
instead of an instance of X. You initialize the reference in the
constructor. In the derived class you initialize it pointing to an
XDerived. |
When you use a reference to X instead of a directly-contained X, there
is
overhead -- it might not be significant, but it's at least the size of
a
pointer. Plus memory allocation is slower because the object is in two
(or
more) discontinuous pieces.
| Quote: | [Jerry Coffin post copied for reference]
I should add that problems can arise with this basic design as well.
In particular, if you can no longer reasonably represent the TYPE of
thing being dealt with as the original type at all, then you have to
make more widespread changes.
|
This is true no matter how the data is exposed: public data,
getters/setters, or the proxy.
However, a void function such as
.. Customer::print_name_and_address(Printer &p);
won't change signatures, even if the name and address are stored in a
radically different form within Customer.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|