Which implementation of "set/get" member functions is better? Please help.

M

Me

Hi, I would like learn from people with experience in C++, which of the
following styles of way to construct "get/set" member functions would be the
best in terms of usability, speed, et cetera.

The following class with be used as an example:
class MyClass {
public:
// member function would go here.
private:
int data_;
};

Way #1:
int getData() const;
void setData(const int);

Way #2:
int getData() const;
int setData(int); // returns data_

Way #3:
int data() const;
void data(int);

Way #4:
int data() const;
int data(int); // returns data_

My preference would be to use the fourth way, but I would like to know which
of the ways listed should be prefered or if there are any other ways that
should have been included.

Also, I notice that people who are into OOA and OOD seem to use getBlah, and
setBlah. Why is this the case? Were most of these individuals influened by
one source and that is why they all share the same way of doing it?

Thanks.
 
J

John Harrison

Me said:
Hi, I would like learn from people with experience in C++, which of the
following styles of way to construct "get/set" member functions would be the
best in terms of usability, speed, et cetera.

The following class with be used as an example:
class MyClass {
public:
// member function would go here.
private:
int data_;
};

Way #1:
int getData() const;
void setData(const int);

Way #2:
int getData() const;
int setData(int); // returns data_

Way #3:
int data() const;
void data(int);

Way #4:
int data() const;
int data(int); // returns data_

My preference would be to use the fourth way, but I would like to know which
of the ways listed should be prefered or if there are any other ways that
should have been included.

Also, I notice that people who are into OOA and OOD seem to use getBlah, and
setBlah. Why is this the case? Were most of these individuals influened by
one source and that is why they all share the same way of doing it?

Thanks.

A fifth way is

int& data();
int data() const;

but I don't like it, returning a reference more or less commits you to using
a member variable internally. Personally I'd use your first method. Get and
set are different enough that they deserve different names. I don't see the
need to return the previous value from set, if I needed that I'd call get
before set. Again this seems a matter of clarity to me, which should be your
main consideration, not speed.

john
 
G

Gianni Mariani

John said:
A fifth way is

int& data();
int data() const;

but I don't like it, returning a reference more or less commits you to using
a member variable internally. Personally I'd use your first method. Get and
set are different enough that they deserve different names. I don't see the
need to return the previous value from set, if I needed that I'd call get
before set. Again this seems a matter of clarity to me, which should be your
main consideration, not speed.

And there is a 6th way which is a variant of the 5th.

AccessorReference<data_attributes> data();
const AccessorReference<data_attributes> data() const;

Where an object of type AccessorReference<data_attributes> has
assignment and cast operators that do what you expect.

It solves the problem you describe "commits you to using a member
variable". This allows you to have a single interface to get and set a
variable regardless if it's a member variable or somthing different.

I've used this technique when you might also have a variant on HOW you
get or set the data.

e.g.

obj.data()(ScopingName) = 1;

obj.data()(NotThrow) = 1;
 
I

Icosahedron

Me said:
Hi, I would like learn from people with experience in C++, which of the
following styles of way to construct "get/set" member functions would be the
best in terms of usability, speed, et cetera.

The following class with be used as an example:
class MyClass {
public:
// member function would go here.
private:
int data_;
};

Way #1:
int getData() const;
void setData(const int);

Way #2:
int getData() const;
int setData(int); // returns data_

Way #3:
int data() const;
void data(int);

My preference is for #3. This the way I do it in my personal code, though
at work we use #1.
Also, I notice that people who are into OOA and OOD seem to use getBlah, and
setBlah. Why is this the case? Were most of these individuals influened by
one source and that is why they all share the same way of doing it?

Maybe because in OOA and OOD circles, being language neutral, they can't
assume that parameters are taken into account for polymorphism? Just a
guess.

Jay
 
C

Corno

Me said:
Hi, I would like learn from people with experience in C++, which of the
following styles of way to construct "get/set" member functions would be the
best in terms of usability, speed, et cetera.

The following class with be used as an example:
class MyClass {
public:
// member function would go here.
private:
int data_;
};

Way #1:
int getData() const;
void setData(const int);

Way #2:
int getData() const;
int setData(int); // returns data_

Way #3:
int data() const;
void data(int);

Way #4:
int data() const;
int data(int); // returns data_
And there is a #0 way.... not using them at all (or at least very rarely).
Sometimes a class of mine has a set function, sometimes a get function but
never the combination of the both. Rethink your design and see if you cannot
let the owning class do the things that need to be done with it's member
variables. It strongly improves the OO.

Corno
 
J

John Harrison

A fifth way is

int& data();
int data() const;
[snip]


And there is a 6th way which is a variant of the 5th.

AccessorReference<data_attributes> data();
const AccessorReference<data_attributes> data() const;

Where an object of type AccessorReference<data_attributes> has
assignment and cast operators that do what you expect.

It solves the problem you describe "commits you to using a member
variable". This allows you to have a single interface to get and set a
variable regardless if it's a member variable or somthing different.

I've used this technique when you might also have a variant on HOW you
get or set the data.

e.g.

obj.data()(ScopingName) = 1;

obj.data()(NotThrow) = 1;

Proxy classes in other words. Perhaps the OP would benefit from a short
example? I think s/he would be interested.

john
 
M

Me

Hi, I must admit that I don't understand the "proxy class" that was
mentioned. Would you mind explaining it? It seems that using it would be
inefficient compared to the other methods, but since I don't know anything
of proxy classes, perhaps it doesn't.

Thanks for the alternatives, both of you.

John Harrison said:
A fifth way is

int& data();
int data() const;
[snip]


And there is a 6th way which is a variant of the 5th.

AccessorReference<data_attributes> data();
const AccessorReference<data_attributes> data() const;

Where an object of type AccessorReference<data_attributes> has
assignment and cast operators that do what you expect.

It solves the problem you describe "commits you to using a member
variable". This allows you to have a single interface to get and set a
variable regardless if it's a member variable or somthing different.

I've used this technique when you might also have a variant on HOW you
get or set the data.

e.g.

obj.data()(ScopingName) = 1;

obj.data()(NotThrow) = 1;

Proxy classes in other words. Perhaps the OP would benefit from a short
example? I think s/he would be interested.

john
 
M

Me

It's interesting to know that your job requires the first way. I didn't
expect to find things like this out.

You're explanation for the reason why OOA/OOD people use get/set in the
names makes sense. I know that most of them know LISP, perhaps that doesn't
have overloading?

Thanks for the feedback.
 
A

Attila Feher

John Harrison wrote:
[SNIP]
A fifth way is

int& data();
int data() const;

but I don't like it, returning a reference more or less commits you
to using a member variable internally.
[SNIP]

Actually in the first version you might use some sort of proxy object -
instead of the int. That you may even return by value (contains a pointer
or reference). However IMHO ANY kind of getter/setter is bad. If something
contains only publicly accassible unchecked variables: make it a struct. If
not: make it something sensible. I can believe that a setVolume and
getVolume function pair is sensible, but they would certainly not
take/return int, but something called Volume...

Attila
 
G

Gianni Mariani

Me said:
Hi, I must admit that I don't understand the "proxy class" that was
mentioned. Would you mind explaining it? It seems that using it would be
inefficient compared to the other methods, but since I don't know anything
of proxy classes, perhaps it doesn't.

Thanks for the alternatives, both of you.

Who invented the name "proxy class" ?


So effienciency need not necessarily be bad. If your proxy class
contructor and accessors are simple enough, the compiler may optimize
them away making it just as fast as anything else.

Just say you had a class with an extreme number of attributes and they
all did basically the same thing but if any of them was changed, you
needed to perform some post processing )e.g. recompute a checksum or
inform a GUI object of the change, write the change to disk, or perhaps
make a "transaction" where you change a number of attributes at once but
you wanted to make the change to be made atomically yet you wanted to
hide all of this and make it look just like getters and setters.

I created a quick chunk of test code that implements the
lock/transaction scenario. In this case we're using the property of how
C++ temporary objects are created and destroyed to our advantage. Hence
any SINGLE C++ expression is transaction safe because temporary objects
are the last thing to be destroyed when executing an expression.
Actually, if you decide to pollute your code with nasty Proxy objects
you could extend the transaction safety beyond a single expression.

Note that the example below DOES NOT TAKE INTO ACCOUNT issues when you
have 2 or more lockable objects. The order in which you take a mutex is
extremely important in avoiding deadlock.

I've never used this paradigm for managing tranactions and I'm not sure
that it makes sense to do it like this because writing expressions and
transaction semantics are not genrally combinable concepts. Having said
that, I'm sure there are some cases where this paradigm may work
perfectly (like reading/writing to shared memory). Also, with a little
more work, you could actually make rollback on exception functional
which IS truly cool.

As for efficiency, as I alluded earlier, the compiler should be able to
inline and remove most of the code that does nothing. Hence from an
efficiency standpoint, this is about as efficient as anything you could
write by hand.

This is just an example of how you can use a proxy. There are so many
more uses. I've used variations on this for factory accessors and
shared memory accessors but they didn't seem to be appropriate examples
given the OP question.

One last point, there is nothing stopping you from using methods GetData
and SetData with proxy classes. My original response to John was just a
clarification on the theme he described.


#include <iostream>

// demo lock - does nothing like locking just yells
struct MutexType
{
int m_lock_count;

MutexType()
: m_lock_count( 0 )
{}

void TakeLock()
{
if ( m_lock_count ++ ) return;
std::cout << "lock is taken\n";
}

void ReleaseLock()
{
if ( -- m_lock_count ) return;
std::cout << "lock is released\n";
}
};

struct Lock
{
Lock( MutexType & i_mutex )
: m_mutex( & i_mutex )
{
m_mutex->TakeLock();
}

~Lock()
{
m_mutex->ReleaseLock();
}

MutexType * m_mutex;
};

template<typename Type> class Proxy
{
public:
Proxy( Type & value, MutexType & i_mutex )
: m_value( value ),
m_lock( i_mutex )
{
}

operator const Type & () const
{
std::cout << "converted to Type\n";
return m_value;
}

// operator =

Proxy & operator= ( const Type & i_val )
{
std::cout << "= Type\n";
m_value = i_val;
return * this;
}

template<typename RHSType> Proxy & operator= ( const Proxy<RHSType>
& i_val )
{
std::cout << "= Proxy<RHSType>\n";
m_value = i_val.get();
return * this;
}

Proxy & operator= ( const Proxy & i_val )
{
std::cout << "= Proxy\n";
m_value = i_val.get();
return * this;
}

// operator +=

Proxy & operator+= ( const Type & i_val )
{
std::cout << "+= Type\n";
m_value += i_val;
return * this;
}

template<typename RHSType> Proxy & operator+= ( const
Proxy<RHSType> & i_val )
{
std::cout << "+= Proxy<RHSType>\n";
m_value += i_val.get();
return * this;
}

Proxy & operator+= ( const Proxy & i_val )
{
std::cout << "+= Proxy\n";
m_value += i_val.get();
return * this;
}

// operator -=

Proxy & operator-= ( const Type & i_val )
{
std::cout << "-= Type\n";
m_value -= i_val;
return * this;
}

template<typename RHSType> Proxy & operator-= ( const
Proxy<RHSType> & i_val )
{
std::cout << "-= Proxy<RHSType>\n";
m_value -= i_val.get();
return * this;
}

Proxy & operator-= ( const Proxy & i_val )
{
std::cout << "-= Proxy\n";
m_value -= i_val.get();
return * this;
}

// etc for all the operators you want

const Type & get() const
{
return m_value;
}

Type & m_value;
Lock m_lock;

};

struct BigNastyAttributeClass
{

int m_val1;
float m_val2;

MutexType m_lock;

Proxy<int> val1()
{
return Proxy<int>( m_val1, m_lock );
}

Proxy<float> val2()
{
return Proxy<float>( m_val2, m_lock );
}

};


void Tester( BigNastyAttributeClass & foo )
{

std::cout << "do 1\n";

foo.val2() = 3 + ( foo.val1() += 3 );

std::cout << "do 2\n";

foo.val2() -= ( foo.val1() += 4 );

std::cout << "do 3\n";

foo.val2() = 4, foo.val1() += 9;

std::cout << "return\n";
}

int main()
{
BigNastyAttributeClass a;

Tester( a );
}

This code spits out:

do 1
lock is taken
+= Type
converted to Type
= Type
lock is released
do 2
lock is taken
+= Type
-= Proxy<RHSType>
lock is released
do 3
lock is taken
= Type
+= Type
lock is released
return

Note how "lock is taken" and "lock is released" neatly wrap assignment
and conversion (getter) operations. I hope this helps.
 
B

Buster Copley

Gianni said:
Who invented the name "proxy class" ?

It's not a name, but a description. Look up 'proxy' in the dictionary.
If you really want to know who first used the term 'proxy class', good
luck in your research.

Regards,
Buster.
 
G

Gianni Mariani

Buster Copley wrote:
....
It's not a name, but a description. Look up 'proxy' in the dictionary.
If you really want to know who first used the term 'proxy class', good
luck in your research.

By that definition, almost every class is a proxy of some kind.

I would call these "property accessor" classes because they control the
access to properties.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top