Tutorial :Is it good to have all the setter functions return a reference to the object in c++?



Question:

Is it good to have all the setter functions return a reference to the object in c++?


Solution:1

It's a usable enough pattern if there's a lot of things that need to be set on an object.

 class Foo   {        int x, y, z;   public:        Foo &SetX(int x_) { x = x_;  return *this; }        Foo &SetY(int y_) { y = y_;  return *this; }        Foo &SetZ(int z_) { z = z_;  return *this; }   };     int main()   {        Foo foo;        foo.SetX(1).SetY(2).SetZ(3);   }  

This pattern replaces a constructor that takes three ints:

 int main()   {        Foo foo(1, 2, 3); // Less self-explanatory than the above version.   }  

It's useful if you have a number of values that don't always need to be set.

For reference, a more complete example of this sort of technique is refered to as the "Named Parameter Idiom" in the C++ FAQ Lite.

Of course, if you're using this for named parameters, you might want to take a look at boost::parameter. Or you might not...


Solution:2

You can return a reference to this if you want to chain setter function calls together like this:

obj.SetCount(10).SetName("Bob").SetColor(0x223344).SetWidth(35);  

Personally I think that code is harder to read than the alternative:

obj.SetCount(10);  obj.SetName("Bob");  obj.SetColor(0x223344);  obj.SetWidth(35);  


Solution:3

IMO setters are a code smell that usually indicate one of two things:

Making A Mountian Out Of A Molehill

If you have a class like this:

class Gizmo  {  public:    void setA(int a) { a_ = a; }    int getA() const { return a_; }      void setB(const std::string & b) { v_ = b; }    std::string getB() const { return b_; }  private:    std::string b_;    int a_;  };  

... and the values really are just that simple, then why not just make the data members public?:

class Gizmo  {  public:    std::string b_;    int a_;  };  

...Much simpler and, if the data is that simple you lose nothing.

Another possibility is that you could be

Making A Molehill Out Of A Mountian

Lots of times the data is not that simple: maybe you have to change multiple values, do some computation, notify some other object; who knows what. But if the data is non-trivial enough that you really do need setters & getters, then it is non-trivial enough to need error handling as well. So in those cases your getters & setters should be returning some kind of error code or doing something else to indicate something bad has happened.

If you are chaining calls together like this:

A.doA().doB().doC();  

... and doA() fails, do you really want to be calling doB() and doC() anyway? I doubt it.


Solution:4

If your motivation is related to chaining (e.g. Brian Ensink's suggestion), I would offer two comments:

1. If you find yourself frequently settings many things at once, that may mean you should produce a struct or class which holds all of these settings so that they can all be passed at once. The next step might be to use this struct or class in the object itself...but since you're using getters and setters the decision of how to represent it internally will be transparent to the users of the class anyways, so this decision will relate more to how complex the class is than anything.

2. One alternative to a setter is creating a new object, changing it, and returning it. This is both inefficient and inappropriate in most types, especially mutable types. However, it's an option that people sometimes forget, despite it's use in the string class of many languages.


Solution:5

The typical purpose for this style is in use for object construction.

Person* pPerson = &(new Person())->setAge(34).setId(55).setName("Jack");  

instead of

Person* pPerson = new Person( 34, 55, "Jack" );  

Using the second more traditional style one might forget if the first value passed to the constructor was the age or the id? This may also lead to multiple constructors based on the validity of some properties.

Using the first style one might forget to set some of the object properties and and may lead bugs where objects are not 'fully' constructed. (A class property is added at a later point but not all the construction locations got updated to call the required setter.)

As code evolves I really like the fact that I can use the compiler to help me find all the places where an object is created when changing the signature of a constructor. So for that reason I prefer using regular C++ constructors over this style.

This pattern might work well in applications that maintain their datamodel over time according to rules similar to those used in many database applications:

  • You can add a field/attribute to a table/class that is NULL by default. (So upgrading existing data requires just a new NULL column in the database.)
  • Code that is not changes should still work the same with this NULL field added.


Solution:6

Not all the setters, but some of them could return reference to object to be useful.

kind of

a.SetValues(object)(2)(3)(5)("Hello")(1.4);  

I used this once long time ago to build SQL expression builder which handles all the Escapes problems and other things.

SqlBuilder builder;    builder.select( column1 )( column2 )( column3 ).      where( "=" )( column1, value1 )                  ( column2, value2 ).      where( ">" )( column3, 100 ).      from( table1 )( "table2" )( "table3" );  

I wasn't able to reproduce sources in 10 minutes. So implementation is behind the curtains.


Solution:7

This technique is used in the Named parameter Idiom.


Solution:8

I would not think so. Typically, you think of 'setter' object as doing just that.

Besides, if you just set the object, dont you have a pointer to it anyway?


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »