Using value overrides and operator () overloading to simplify getter / setter design: a dangerous practice?

Consider the following class:

class MyClass1 { public: double x() const {return _x;} // getter double y() const {return _y;} // getter double z() const {return _x*_y;} // getter void x(const double var) {_x = var;} // setter void y(const double var) {_y = var;} // setter void z(const double var) {_x = var; _y = 1;} // setter protected: double _x; double _y; }; 

Since the actual content of MyClass1 is an implementation detail, getters and setters provide a single way to get and set the contents of the class, even if they are interdependent (here _z does not exist inside, and for the user, z is a variable of type x and y ).

Now, to avoid having to write getter / setter for x and y , you can use the wrapper as follows:

 template <typename Type> class Wrapper { public: constexpr Wrapper(const Type& value) {_value = value;} constexpr Type& operator()() {return _value;} constexpr const Type& operator()() const {return _value;} constexpr void operator()(const Type& value) {_value = value;} protected: _value; }; 

And now the source class will look like this:

 class MyClass2 { public: Wrapper<double> x; Wrapper<double> y; double z() const {return x*y;} // getter void z(const double var) {x = var; y = 1;} // setter }; 

Is this a dangerous practice or a good solution to avoid having to write getters / setters?

Note. Here MyClass1 and MyClass2 are just examples. My question is very "general": is it dangerous to replace getters / setters of classes with the proposed Wrapper when the getter / setter just returns / sets the internal value.

+6
source share
3 answers

As you mentioned, the main goal of getters and setters is to provide a single way to access and set your private instance variables.

From your wrapper solution, if the life cycle of the program ever falls, you decide to change the setter, you can simply throw away the shell and replace it with the original getter / setter, as in MyClass1 - without having to change all other components of the code that calls it .

So, from now on, I think your solution is a legitimate way to save you from entering extra lines of code.

+1
source

I don’t see anything particularly dangerous there, but it seems to get nothing. C ++ provides reference semantics for any object that is incompatible with the setter function. Sometimes the actual contents are not just details.

You can also go the other way (actually this is what I expected to see when I clicked this question):

 struct virt_z { double x; double y; struct z_wrap { virt_z &parent; operator double() { return parent.x * parent.y; } z_wrap &operator=( double in ) { parent.x = in; parent.y = 1; return *this; } z_wrap( virt_z &in ) : parent( in ) {} } z; virt_z() : z( *this ) {} virt_z( virt_z const &o ) : x( ox ), y( oy ), z( *this ) {} }; 

https://ideone.com/qqgj3D

+1
source

This is not dangerous as such, it is simply harder to use (for the class user). MyClass2 confuses and clutters the interface.

This may make you less tiring to write code, but you only need to write the code once - the interface will be reused. Therefore, the code reader should be preferred.

MyClass1 for this reason is superior to both MyClass2 and Potatoswatter.

(Even if there was a way to do this in C ++:

 class MyClass3 { double x, y, z; } MyClass3::z::operator=(double) { ... } // property set double MyClass3::z::operator() { ... } // property get 

as in C #, it would still be a bad idea, because such β€œhidden” code leads to poor assumptions that can slow down debugging. At least the style of the function allows us to think that there might be something more complex than a primitive copy.)

+1
source

All Articles