Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

public member variable vs. get/set functions

161 views
Skip to first unread message

W Karas

unread,
Dec 9, 2013, 5:39:12 PM12/9/13
to
The main purpose of private members is to provide some level of compile time checking for the rules of use of an object that must be followed, in order to keep the object in a valid state.

Given that, a private member variable with (simple, public) get/set functions just seems like pointless code bloat. It provides no additional protection against putting the object in a bad state than having the member variable be public.

Some (perhaps Smalltalk fans) argue that it's very rare that simply setting a member variable should properly be part of an objects public interface. Therefore, it's best to follow the rule of thumb that all member variables should be private.

It can also be argued that it is often important, for code maintenance, to be able to quickly identify external changes to a member variable, as opposed to read access to the variable. A counter-argument is that some sort of code browsing tool should be available to prevent the need to bloat the code in order to highlight this distinction.

In some cases, it could be likely that the get/set function would eventually do more than simple get/set a single member variable. That would be a clear case where keeping the member variable private would be the correct choice. But, as I have seen Dr. Bjarne argue, it's easy to go overboard with future-safety.

Your thoughts?

Marcel Müller

unread,
Dec 9, 2013, 6:07:56 PM12/9/13
to
On 09.12.13 23.39, W Karas wrote:
> The main purpose of private members is to provide some level of compile time checking for the rules of use of an object that must be followed, in order to keep the object in a valid state.
>
> Given that, a private member variable with (simple, public) get/set functions just seems like pointless code bloat. It provides no additional protection against putting the object in a bad state than having the member variable be public.

It also creates no overhead in an optimized build as long as your
getter/setter is implemented inline.

> Some (perhaps Smalltalk fans) argue that it's very rare that simply setting a member variable should properly be part of an objects public interface. Therefore, it's best to follow the rule of thumb that all member variables should be private.
>
> It can also be argued that it is often important, for code maintenance, to be able to quickly identify external changes to a member variable, as opposed to read access to the variable. A counter-argument is that some sort of code browsing tool should be available to prevent the need to bloat the code in order to highlight this distinction.

Eclipse CDT can mostly distinguish between read and write access in the
references view.

> In some cases, it could be likely that the get/set function would eventually do more than simple get/set a single member variable. That would be a clear case where keeping the member variable private would be the correct choice. But, as I have seen Dr. Bjarne argue, it's easy to go overboard with future-safety.

If you intend to change the behavior in future versions using
getters/setters might keep source code compatibility.

> Your thoughts?

I usually do not use simple getters/setters. But I usually also do not
need public member variables, except for const ones.


Marcel

Victor Bazarov

unread,
Dec 9, 2013, 6:18:24 PM12/9/13
to
On 12/9/2013 5:39 PM, W Karas wrote:
> The main purpose of private members is to provide some level of
> compile time checking for the rules of use of an object that must be
> followed, in order to keep the object in a valid state.

<shrug> I probably wouldn't call it "main purpose", but we can take it
as an assumption.

> Given that, a private member variable with (simple, public) get/set
> functions just seems like pointless code bloat. It provides no
> additional protection against putting the object in a bad state than
> having the member variable be public.

"Simple"? You mean, "obvious" or "apparent"? Well, perhaps. You can
call it a "placeholder", though, and then it's not necessarily so simple
anymore. Later you can decide to make it more complex, add validation
(if that's what you're after), and then it's not *at all* the same as
having a public member.

> Some (perhaps Smalltalk fans) argue that it's very rare that simply
> setting a member variable should properly be part of an objects
> public interface. Therefore, it's best to follow the rule of thumb
> that all member variables should be private.
>
> It can also be argued that it is often important, for code
> maintenance, to be able to quickly identify external changes to a
> member variable, as opposed to read access to the variable. A
> counter-argument is that some sort of code browsing tool should be
> available to prevent the need to bloat the code in order to highlight
> this distinction.

If those getter/setter functions are "simple", as you call them, then
the "code bloat" is not significant, and from the code maintenance POV
it's better to have a function that gets inlined by the compiler than
have the name of the member data strewn around the rest of the user
code, which also carries the danger of pointer or reference retention
and can cause other kinds of trouble.

> In some cases, it could be likely that the get/set function would
> eventually do more than simple get/set a single member variable.
> That would be a clear case where keeping the member variable private
> would be the correct choice. But, as I have seen Dr. Bjarne argue,
> it's easy to go overboard with future-safety.
>
> Your thoughts?

It's easy to go overboard with anything one does. Still, a little
planning ahead goes usually a long way. If you think you need to ask
the "why do it if we don't need it" question, then don't forget to ask
the "what's it going to cost to add it later if we do need it" as well.

V
--
I do not respond to top-posted replies, please don't ask

W Karas

unread,
Dec 9, 2013, 6:29:28 PM12/9/13
to
On Monday, December 9, 2013 6:07:56 PM UTC-5, Marcel Müller wrote:
> On 09.12.13 23.39, W Karas wrote:
>
> > The main purpose of private members is to provide some level of compile time checking for the rules of use of an object that must be followed, in order to keep the object in a valid state.
>
> >
>
> > Given that, a private member variable with (simple, public) get/set functions just seems like pointless code bloat. It provides no additional protection against putting the object in a bad state than having the member variable be public.
>
>
>
> It also creates no overhead in an optimized build as long as your getter/setter is implemented inline.

Correct in that it creates no object code bloat. But does still create source code bloat (size increase).

Chris Uzdavinis

unread,
Dec 9, 2013, 6:53:08 PM12/9/13
to
On Monday, December 9, 2013 5:29:28 PM UTC-6, W Karas wrote:

>
> > It also creates no overhead in an optimized build as long as your getter/setter is implemented inline.
>
>
>
> Correct in that it creates no object code bloat. But does still create source code bloat (size increase).


It has been said to not worry so much about the implementation (as that can always be fixed if it's wrong) but to worry about the interface. Get that wrong and you may be stuck forever.

Don't forget about the possibility of changing the underlying representation of your data. If you have public data then you are permanently locked into that implementation (unless you are lucky enough to be able to track down every use of your class. Unless you're your only customer, that can be very difficult.)

It's usually a bad idea to expose implementation details in your interface because once exposed they cannot be changed. If you realize you made a poor decision and want to represent them differently, too bad.


If there is even the slightest chance that in the future you may need to intervene with the returned value, or set a breakpoint, or count the number of accesses, add validation, or do any of a handful of other things with that point of access of your data, with the accessors you can.

A few lines of inline accessor code buys you so much, and is IMHO a very low price to pay.

Chris


chris

W Karas

unread,
Dec 9, 2013, 7:34:37 PM12/9/13
to
On Monday, December 9, 2013 6:53:08 PM UTC-5, Chris Uzdavinis wrote:
...
> If there is even the slightest chance that in the future you may need to intervene with the returned value, or set a breakpoint, or count the number of accesses, add validation, or do any of a handful of other things with that point of access of your data, with the accessors you can.
>
> A few lines of inline accessor code buys you so much, and is IMHO a very low price to pay.
...

The point about breakpoints is a good one. But again, that can be seen as bad compensation for bad tools. The debugger should support data breakpoints. Also, you could temporarily rewrite:

int i;

as:

struct I { int i; operator int & () { return(i); } }; I i;

W Karas

unread,
Dec 9, 2013, 7:41:35 PM12/9/13
to
On Monday, December 9, 2013 6:53:08 PM UTC-5, Chris Uzdavinis wrote:
...
> A few lines of inline accessor code buys you so much, and is IMHO a very low price to pay.
...

class Point
{
private: double x, y;
public:
void set_x(double x_) { x = x_; }
double get_x() { return(x); }
void set_y(double y_) { y = y_; }
double get_y() { return(y); }
};

The fact that you probably threw up a little in your mouth when you read this code indicates why I think you should consider maintaining some flexibility in your point of view on this.

Ian Collins

unread,
Dec 9, 2013, 7:52:18 PM12/9/13
to
W Karas wrote:
> On Monday, December 9, 2013 6:53:08 PM UTC-5, Chris Uzdavinis wrote:
> ....
>> A few lines of inline accessor code buys you so much, and is IMHO a
>> very low price to pay.
> ....
>
> class Point { private: double x, y; public: void set_x(double x_) { x
> = x_; } double get_x() { return(x); } void set_y(double y_) { y = y_;
> } double get_y() { return(y); } };
>
> The fact that you probably threw up a little in your mouth when you
> read this code indicates why I think you should consider maintaining
> some flexibility in your point of view on this.

* Please clean up the mess that shite google interface makes of you
quotes. *

If you have retained more of the original context, you might have
appreciated the point Chris Uzdavinis made.

Ignoring the case where point is just a trivial struct, one might have

class Point
{
double x, y;

void sanityCheck( double );

public:

void y(double y_) { sanityCheck(y_); y = y_; }
void x(double x_) { sanityCheck(x_); x = x_; }

double x() const { return(x); }
double y() const { return(y); }
};

--
Ian Collins

Öö Tiib

unread,
Dec 10, 2013, 10:08:54 AM12/10/13
to
On Tuesday, 10 December 2013 00:39:12 UTC+2, W Karas wrote:

> Some (perhaps Smalltalk fans) argue that it's very rare that simply setting a member variable
> should properly be part of an objects public interface. Therefore, it's best to follow the rule
> of thumb that all member variables should be private.

What is that "simply setting"? Can you bring lot of "simply setting a member variable" examples
from C++ standard library? If you have difficulties then maybe C++ library is made by
"Smalltalk fans"?

Jorgen Grahn

unread,
Dec 10, 2013, 2:44:36 PM12/10/13
to
On Mon, 2013-12-09, Chris Uzdavinis wrote:

> It has been said to not worry so much about the implementation (as
> that can always be fixed if it's wrong) but to worry about the
> interface. Get that wrong and you may be stuck forever.

But that's almost never true! It /is/ true if you're writing
libraries and handing them out, but almost noone does that.

It took me many years to realize that
(a) I'm writing programs, not building a cathedral
(b) I can always refactor something later if it's important to.
(c) The static nature of C++ makes (b) rather easy: change the
interface; hack the client code until it compiles again;
change the inner implementation until that also compiles again.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

W Karas

unread,
Dec 10, 2013, 5:05:46 PM12/10/13
to
...

But what you're saying seems to go against the O-O philosophy. Which says you should try to build your program as a set of cooperating objects. The objects should have a "natural" interface that can be based on the nature of the object, rather than the particular purpose of the program. Thus, this interface can be reused (ideally) in all programs involving objects of this type. This implies that the interface is used in lots of places. Implying there is a big multiplier from the amount of interface change to the amount of other consequential change.

Scott Lurndal

unread,
Dec 10, 2013, 5:26:07 PM12/10/13
to
(b) can become difficult when the API in question is provided by
a third-party library, for which one does not have source. Using
the getter-setter paradigm in the library allows the library vendor
to make enhancements to the API without actually changing it.

Jorgen Grahn

unread,
Dec 10, 2013, 5:31:35 PM12/10/13
to
I don't quite understand what you're getting at here ... note that
I explicitly left out the library designer case.

Jorgen Grahn

unread,
Dec 10, 2013, 6:01:20 PM12/10/13
to
On Tue, 2013-12-10, W Karas wrote:
> On Tuesday, December 10, 2013 2:44:36 PM UTC-5, Jorgen Grahn wrote:
>> On Mon, 2013-12-09, Chris Uzdavinis wrote:
>> > It has been said to not worry so much about the implementation (as
>> > that can always be fixed if it's wrong) but to worry about the
>> > interface. Get that wrong and you may be stuck forever.
>> But that's almost never true! It /is/ true if you're writing
>> libraries and handing them out, but almost noone does that.

>> It took me many years to realize that
>> (a) I'm writing programs, not building a cathedral
>> (b) I can always refactor something later if it's important to.
>> (c) The static nature of C++ makes (b) rather easy: change the
>> interface; hack the client code until it compiles again;
>> change the inner implementation until that also compiles again.
> ...

> But what you're saying seems to go against the O-O philosophy.

Maybe. I don't feel I have to follow someone else's philosophy.
Besides, it's not 1992 anymore. People don't seem to take object
orientation that seriously.

> Which says you should try to build your program as a set of
> cooperating objects.

So far, so good.

> The objects should have a "natural" interface
> that can be based on the nature of the object, rather than the
> particular purpose of the program.

But that last part ... that idea has caused so much damage! I have not
seen it stated explicitly before, but I've seen a lot of designs
warped by it.

A program should be like a movie set: it may look like a western town
or the Death Star or whatever, but the parts not shown on film are
never built!

> Thus, this interface can be reused
> (ideally) in all programs involving objects of this type. This
> implies that the interface is used in lots of places. Implying there
> is a big multiplier from the amount of interface change to the amount
> of other consequential change.

Yes, but as we all know that's untrue, unless you're a successful
library designer.

Are you trolling me in a mild way, by the way? Do you have an opinion
on this subject?

W Karas

unread,
Dec 10, 2013, 6:44:36 PM12/10/13
to
On Tuesday, December 10, 2013 6:01:20 PM UTC-5, Jorgen Grahn wrote:
> On Tue, 2013-12-10, W Karas wrote:
> > But what you're saying seems to go against the O-O philosophy.
>
> Maybe. I don't feel I have to follow someone else's philosophy.

OK, that's safe to do in the nicer countries of the world.

> Besides, it's not 1992 anymore. People don't seem to take object
> orientation that seriously.
>
> > Which says you should try to build your program as a set of
> > cooperating objects.
>
> So far, so good.
>
> > The objects should have a "natural" interface
> > that can be based on the nature of the object, rather than the
> > particular purpose of the program.
>
> But that last part ... that idea has caused so much damage! I have not
> seen it stated explicitly before, but I've seen a lot of designs
> warped by it.

The existence of non-nail fasteners implies neither that all nor none of one's tools should be hammers.

Sometimes you'd define the full "natural" interface and do a full implementation.

Sometimes you'd define the full "natural" interface, but do not implement the parts not immediately needed.

Sometimes you'd define just the part of the "natural" interface that you need at that time.

Sometime your boss won't let you leave the building till it's done, so you just worry about the program you're currently working on.

Almost any virtue in excess becomes a vice. Fortunately, Dr. Bjarne is aware of this, so in C++ functions that are not objects do not have to pretend that they are.

>
> A program should be like a movie set: it may look like a western town
> or the Death Star or whatever, but the parts not shown on film are
> never built!

Unless the other side of the building is likely to appear in your next movie.

>
> > Thus, this interface can be reused
> > (ideally) in all programs involving objects of this type. This
> > implies that the interface is used in lots of places. Implying there
> > is a big multiplier from the amount of interface change to the amount
> > of other consequential change.

>
> Yes, but as we all know that's untrue, unless you're a successful
> library designer.

O-O and generic programming work to make successful library designers of us all.

>
> Are you trolling me in a mild way, by the way? Do you have an opinion
> on this subject?

Stop troll-trolling.

Richard Damon

unread,
Dec 11, 2013, 8:55:39 AM12/11/13
to
On 12/10/13, 6:44 PM, W Karas wrote:
> On Tuesday, December 10, 2013 6:01:20 PM UTC-5, Jorgen Grahn wrote:
>> On Tue, 2013-12-10, W Karas wrote:
>>> But what you're saying seems to go against the O-O philosophy.
>>
>> Maybe. I don't feel I have to follow someone else's philosophy.
>
> OK, that's safe to do in the nicer countries of the world.
>

My own philosophy is to divide my code into the "application specific"
pieces and the "generic" (library/framework) pieces. Generic piece I try
to reuse, and make reusable between multiple programs. I try to
establish an API with them that will at least be as backwards compatible
as possible, so if I pick up an old project, I don't get too surprised
with changes. At the very least, I make sure there is no "quiet"
incompatibilities.

For application code, I don't worry so much about reuse (I don't ignore
it either, but it isn't a "key" requirement). API here is more able to
be in flux as needed. If I find that I start to be reusing an
application chunk multiple places, I will take the time to clean up the
interface if needed (maybe needing some refactoring), and put a not in
the old files about perhaps wanting to migrate their use to the new
library version. This also gives me the chance to really understand
needs to do, so I can get a clearer idea of what is needed in the API.

Martijn Lievaart

unread,
Dec 11, 2013, 12:57:07 PM12/11/13
to
On Tue, 10 Dec 2013 14:05:46 -0800, W Karas wrote:

> On Tuesday, December 10, 2013 2:44:36 PM UTC-5, Jorgen Grahn wrote:
>> On Mon, 2013-12-09, Chris Uzdavinis wrote:
>> > It has been said to not worry so much about the implementation (as
>> > that can always be fixed if it's wrong) but to worry about the
>> > interface. Get that wrong and you may be stuck forever.
>> But that's almost never true! It /is/ true if you're writing libraries
>> and handing them out, but almost noone does that.
>> It took me many years to realize that (a) I'm writing programs, not
>> building a cathedral (b) I can always refactor something later if it's
>> important to.
>> (c) The static nature of C++ makes (b) rather easy: change the
>> interface; hack the client code until it compiles again; change the
>> inner implementation until that also compiles again.
> ...
>
> But what you're saying seems to go against the O-O philosophy. Which

Not at all!

> says you should try to build your program as a set of cooperating
> objects. The objects should have a "natural" interface that can be
> based on the nature of the object, rather than the particular purpose of

True, but insights change.

> the program. Thus, this interface can be reused (ideally) in all
> programs involving objects of this type. This implies that the

The promise of re-use. How well has that worked for you?

> interface is used in lots of places. Implying there is a big multiplier

How often do you really re-use an interface without copy-paste of the
implementation?

> from the amount of interface change to the amount of other consequential
> change.

In reality, objects are often tied to a specific program. A set of
programs may use a custom library, and then the use of the interface is a
bit higher, but still manageable on change.

The true power of good O-O (in this context) comes of separation of
interface and implementation, so you CAN change the interface and match
the clients and library to match the new interface, without worrying (too
much) you missed some detail. And if it does not work, debugging is
easier, thanks to a clearly defined interface.

But re-usable libraries of O-O code are best left to the experts and
downloaded from boost.

M4

W Karas

unread,
Dec 11, 2013, 2:13:24 PM12/11/13
to
I will take the point as far as conceding that reuse is not the primary benefit of the O-O approach.

All programs are finite state machines, the state being the value of all of the program's data memory. But for non-trivial programs, it's beyond the capacity of the human mind to deal with a program described as a finite state machine. Programming languages and techniques should enable humans to describe programs with minimal errors. An object cannot be put into an invalid state, so long as it's interface rules are followed. "Seeing" the correctness of the program state changes becomes easier, as the state is the concatenation of the states of a set of objects. (The size of the set is limited as much as possible by object containment hierarchies.) But this only works if the correctness of the object implementation can be evaluated in isolation (from the program). I don't see how that would be the case, if the definition of the object's interface is mostly driven by the specifics of the program (or containing object).

woodb...@gmail.com

unread,
Dec 11, 2013, 2:31:33 PM12/11/13
to
On Wednesday, December 11, 2013 11:57:07 AM UTC-6, Martijn Lievaart wrote:

>
> But re-usable libraries of O-O code are best left to the experts and
> downloaded from boost.
>

I think some of Boost is good. However, I believe the
C++ Middleware Writer has a number of advantages over
the serialization library in Boost.


Brian
Ebenezer Enterprises - So far G-d has helped us.
http://webEbenezer.net

W Karas

unread,
Dec 11, 2013, 2:48:36 PM12/11/13
to

Öö Tiib

unread,
Dec 11, 2013, 3:26:39 PM12/11/13
to
Is 'std::pair' sole example? Then considering the size of standard
library it qualifies as "very rarely". Also it supports your point
that on such rare circumstances writing getters and setters is waste
of time.

W Karas

unread,
Dec 11, 2013, 3:37:21 PM12/11/13
to
It surprises me that the real and imaginary components are not public variables in the <complex> class template. Very unclear to me the value of making them private.

Öö Tiib

unread,
Dec 11, 2013, 4:23:58 PM12/11/13
to
It is nowhere said that 'std::complex<float>' must be implemented as
a class with two float members. The processor may have native support
for complex arithmetic and 'std::complex' may internally use it or
it may be is more efficient to implement it as array of two floats
internally for whatever reasons.

Seungbeom Kim

unread,
Dec 13, 2013, 6:47:58 PM12/13/13
to
That used to be true. However, with the adoption of LWG Issue #387[1],
I doubt if there could be any other way to implement std::complex<T>
than to have two data members of type T.

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#387

--
Seungbeom Kim

Alf P. Steinbach

unread,
Dec 13, 2013, 10:51:38 PM12/13/13
to
Thanks for that reference & info, and ... I think you mean, "two data
members or an array of two values of".

The `reinterpret_cast` in the LWG issue spec most naturally suggests an
array, which would have to be the first member in order to have an
implementation that only relies on standard C++ functionality
(reinterpret_cast to/from first member of suitably restricted class is
supported in general for C compatibility).

The lack of a specification of size means that low level / C language
access to arrays of complex values is still not supported. That's a big
surprise. I can't believe that that's intentional.


Cheers,

- Alf

Richard Damon

unread,
Dec 13, 2013, 11:57:52 PM12/13/13
to
The issue did say:

Moreover, if a is an expression of pointer type cv complex<T>* and the
expression a[i] is well-defined for an integer expression i then:

reinterpret_cast<cv T*>(a)[2*i] designates the real part of a[i]; and
reinterpret_cast<cv T*>(a)[2*i+1] designates the imaginary part of a[i].

which effectively requires sizeof(complex<T>) = 2*sizeof(T)

Alf P. Steinbach

unread,
Dec 14, 2013, 4:28:28 AM12/14/13
to
Thanks, I didn't see the correct parenthesification.

Cheers,

- Alf

Seungbeom Kim

unread,
Dec 15, 2013, 2:04:59 AM12/15/13
to
On 2013-12-13 19:51, Alf P. Steinbach wrote:
> On 14.12.2013 00:47, Seungbeom Kim wrote:
>>
>> That used to be true. However, with the adoption of LWG Issue #387[1],
>> I doubt if there could be any other way to implement std::complex<T>
>> than to have two data members of type T.
>>
>> [1] http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#387
>
> Thanks for that reference & info, and ... I think you mean, "two data
> members or an array of two values of".

That's right. (I'm not sure if elements of a member array can officially
be considered as data members as well. :))

> The `reinterpret_cast` in the LWG issue spec most naturally suggests
> an array, [...]

Not necessarily, though an array is certainly possible. The standard
probably does not guarantee that two data members of the same type
declared consecutively without any intervening access specifiers are
allocated adjacently, but I believe many common implementations do it,
and an implementation can ensure that for std::complex (by any magic
if necessary) since it is part of the implementation anyway.

--
Seungbeom Kim
0 new messages