Getters and setters

You might have noticed that once we slap private onto the Player class definition, we can no longer read or write the name of the player from outside the Player class.

If we try and read the name with the following code:

Player me;
cout << me.name << endl;

Or write to the name, as follows:

me.name = "William";

Using the struct Player definition with private members, we will get the following error:

main.cpp(24) : error C2248: 'Player::name' : cannot access private member declared in class 'Player'

This is just what we asked for when we labeled the name field private. We made it completely inaccessible outside the Player class.

Getters

A getter (also known as an accessor function) is used to pass back copies of internal data members to the caller. To read the player's name, we'd deck out the Player class with a member function specifically to retrieve a copy of that private data member:

class Player
{
private:
  string name;  // inaccessible outside this class!
                //  rest of class as before
public:
  // A getter function retrieves a copy of a variable for you
  string getName()
{
  return name;
}
};

So now it is possible to read the player's name information. We can do this by using the following code statement:

cout << player.getName() << endl;

Getters are used to retrieve private members that would otherwise be inaccessible to you from outside the class.

Tip

Real world tip–Keyword const

Inside a class, you can add the const keyword to a member function declaration. What the const keyword does is promises to the compiler that the internal state of the object will not change as a result of running this function. Attaching the const keyword will look something like this:

string getName() const
{
  return name;
}

No assignments to data members can happen inside a member function that is marked const. As the internal state of the object is guaranteed not to change as a result of running a const function, the compiler can make some optimizations around function calls to const member functions.

Setters

A setter (also known as a modifier function or mutator function) is a member function whose sole purpose is to change the value of an internal variable inside the class, as shown in the following code:

class Player
{
private:
  string name;  // inaccessible outside this class!
                //  rest of class as before
public:
  // A getter function retrieves a copy of a variable for you
  string getName()
{
  return name;
}
void setName( string newName )
{
  name = newName;
}
};

So we can still change the private function of a class from outside the class function, but only if we do so through a setter function.

But what's the point of get/set operations?

So the first question that crosses a newbie programmer's mind when he first encounters get/set operations on private members is, isn't get/set self-defeating? I mean, what's the point in hiding access to data members when we're just going to expose that same data again in another way? It's like saying, "You can't have any chocolates because they are private, unless you say please getMeTheChocolate(). Then, you can have the chocolates."

Some expert programmers even shorten the get/set functions to one liners, like this:

string getName(){ return name; }
void setName( string newName ){ name = newName; }

Let's answer the question. Doesn't a get/set pair break encapsulation by exposing the data completely?

The answer is twofold. First, get member functions typically only return a copy of the data member being accessed. This means that the original data member's value remains protected and is not modifiable through a get() operation.

Set() (mutator method) operations are a little bit counterintuitive though. If the setter is a passthru operation, such as void setName( string newName ) { name=newName; }, then having the setter might seem pointless. What is the advantage of using a mutator method instead of overwriting the variable directly?

The argument for using mutator methods is to write additional code before the assignment of a variable to guard the variable from taking on incorrect values. Say, for example, we have a setter for the hp data member, which will look like this:

void setHp( int newHp )
{
  // guard the hp variable from taking on negative values
  if( newHp < 0 )
  {
    cout << "Error, player hp cannot be less than 0" << endl;
    newHp = 0;
  }
  hp = newHp;
}

The mutator method is supposed to prevent the internal hp data member from taking on negative values. You might consider mutator methods a bit retroactive. Should the responsibility lie with the calling code to check the value it is setting before calling setHp( -2 ), and not let that only get caught in the mutator method? Can't you use a public member variable and put the responsibility for making sure the variable doesn't take on invalid values in the calling code, instead of in the setter? You can.

However, this is the core of the reason behind using mutator methods. The idea behind mutator methods is so that the calling code can pass any value it wants to the setHp function (for example, setHp( -2 )), without having to worry whether the value it is passing to the function is valid or not. The setHp function then takes the responsibility of ensuring that the value is valid for the hp variable.

Some programmers consider direct mutator functions such as getHp()/setHp() a code smell. A code smell is in general a bad programming practice that people don't overtly take notice of, except for a niggling feeling that something is being done suboptimally. They argue that higher-level member functions can be written instead of mutators. For example, instead of a setHp() member function, we should have public member functions such as heal() and damage() instead. An article on this topic is available at http://c2.com/cgi/wiki?AccessorsAreEvil.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.81.201