Z
Zap
[I already posted the following to comp.lang.c++.moderated and received
2 replies (which you can go and see if you are interested). I'm looking
for more discussion by posting again here.]
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&[email protected]&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 ([email protected])
Subject: Re: Member name style
View: Complete Thread (119 articles)
Original Format
Newsgroups: comp.lang.c++.moderated
Date: 1998/02/06
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.
2 replies (which you can go and see if you are interested). I'm looking
for more discussion by posting again here.]
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&[email protected]&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 ([email protected])
Subject: Re: Member name style
View: Complete Thread (119 articles)
Original Format
Newsgroups: comp.lang.c++.moderated
Date: 1998/02/06
[email protected] says... said: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.