Section 4 - Instance Members and Methods

6B.4.1 General Member Methods

Besides constructors and accessor methods, there are other types of member methods. If these methods do not have the keyword static in front of them, they are called instance methods and are called through an object dereference.  If they do have the keyword static in front of their declaration, they are called static class method (or sometimes just class methods) and are called through a dereference to the class name (but no special object).  We will talk about static methods next time, but now let's look deeper into instance methods.

An instance method is called using an object to dereference it, as in:

someObject.instanceMethod();

Once inside the definition of this method, instanceMethod(), we can manipulate the private fields of that particular object.  A good example of an instance method is the Galaxy mutator setMagnitude(), since it is called using an object (gal1 in this case) and is intended to modify the private data of that object (the magnitude field)::

gal1.setMagnitude(100);

In contrast, if we have a static class method, it is called like so:

ClassName::classMethod();

Since there is no object used to call classMethod(), we cannot, inside the definition of classMethod(), refer to any of the class's fields.  That makes sense because there is no object lying around to which that instance member data could belong.

Unfortunately, we haven't had time to see any examples of static methods yet, but we will soon.

This is a little dizzying, I know. 

Let me try to summarize:

6B.4.2 Accessing Member Data With and Without Object References

In the Pet and Employee examples from last week we declared all the data using public access.  This allowed main() to dereference the member data, directly:

birkoff.age = 26;

or

mikesDog.petsName = "Jerry";

We could do that last week because we made all member data public.   This week, however (and from now on!) we are declaring all the member data to be in the private section so main() cannot get to the data directly.  Instead it must use objects to dereference mutator methods like setAge()  or setPetsName() .  Further up you saw me do that with gal1.setMagnitude(100).

In all cases, though, main() needs to use some object to make the change.  Whether the data was public (and we were setting the data manually from main()) or it was private (and we had to use a mutator).  Either way, we needed a specific object to do something.  This makes sense, since we need to know which Employee's age we are talking about or which Galaxy's magnitude we want to set.  We obviously need a specific Employee or Galaxy object in our hand:  birkoff.age = ..., or gal1.setMagnitude( ...).

So far, so good.  But in the Galaxy example, we notice  something odd.  Look at the definition of any of our methods inside the Galaxy class.  Take the getName() method as an example:

string Galaxy::getName()
{
return name;
}

Sure, we are returning the value stored in the name field, but ...which name?  Whose name?  gal1's namegal2's name?  How do we know which one to use?  Another example is in the constructor:

Galaxy::Galaxy()
{
name = "undefined";
magnitude = 0.0;
}

To which name and magnitude are we referring in there?

Stay calm, stand back, look at the big picture, and all will be revealed. 

Here are the clues:

So in getName( ), the name referred to inside the method definition is the name of whatever object did the calling (i.e., whatever object was used to dereference the method call).

Same with the constructor.  We would only get inside the constructor method if we were sent there by the client instantiating a particular object.  When the client instantiates gal1, then it is gal1's data that is initialized inside the constructor.  When gal2 is being instantiated, gal2's data is initialized.

6B.4.3 Instance Methods Calling Instance Methods

Here is a dumb little program, but one that makes a point. 

#include <iostream>
#include <string>
using namespace std;

// Employee class prototype ---------------------
class Employee
{
private:
int socSec;
int age;
void clearSocSec();

public:

// mutators and accessors
bool setAge(int a);
bool setSS(int ss);
int getAge() { return age; }
int getSS() { return socSec; }
bool getOlder();
};

// main method ---------------------------------
int  main()
{
Employee walter;
int k;

walter.setSS(123456789);
walter.setAge(140);

for (k = 0; k < 14; k++)
if ( walter.getOlder() )
cout <<"Happy Birthday!\n";
else
cout << "You must be dead.  \nI'm giving"
<< " your social security number"
<< " to someone else.\n";

return 0;
}

// -------  clearSocSec Definition --------------
void Employee::clearSocSec()
{
socSec = 000000000;
}

// -------  setAge Definition --------------
bool Employee::setAge(int a)
{
if (a < 0 )
// don't allow negative age
return false;
else
{
age = a;
return true;
}
}

// -------  setSS Definition --------------
bool Employee::setSS(int ss)
{
if (ss < 0 )
// don't allow negative ss
return false;
else
{
socSec = ss;
return true;
}
}

// ------- Employee::getOlder -------
bool Employee::getOlder()
{
if (age++ > 147 )
{
clearSocSec();
return false;
}
else
return true;
}
console shot

Notice that this is similar to the first Employee example we had, but

  1. now the data inside the Employee class is private, and
  2. there are some public methods that access the private data as well as a private instance method.

Notice that one of these methods calls another, and it does so without using an object to dereference the called method. That is, inside getOlder() we see a call to clearSocSec() without the expected obj.clearSocSec(). Not only that, but clearSocSec() is private, so we wonder if it can be called at all! Confused?

I'll try to straighten you out.

The reason that the call clearSocSec() inside the function getOlder() doesn't need to be dereferenced by an object was actually answered earlier. Since some object called getOlder() originally, there already is an object which owns all of the unattached data members referenced inside the method. This goes for member method calls inside the method as well.

Note:

From an instance method, any further calls to other instance methods are understood to be owned by the original object.

Thus, walter.getOlder() inside main() will send control to getOlder(). From there, the unqualified call to clearSocSec() is understood to also come through the object walter. We say that we are accessing the object called this (the implicit reference to the calling object). Now, the whole concept of one member function calling another member function should make sense. The original call establishes the "this" object through which all lower level calls (if any) are to be made. Of course once the original call returns back to main(), we would need another object to get into an instance method.

As for the fact that clearSocSec() is private, and getOlder() is able to call it -- well that, again, is what member function do. They can not only change private data, but they can call private methods. There may be methods that we do not want our client to be able to call -- they are convenience functions for our other instance methods. We make those methods private by using the keyword of the same name: private.

6B.4.4 Employee Constructor

If you look at the Employee class we defined, you'll see that I left out a constructor. This is not a compiler error, but it is not a good idea either.  I should have supplied one.  Without one, the Employee objects' instance members will be garbage and unpredictable until we call a mutator method to set them.  However, I'm assuming that you can follow the example of the Galaxy class and write your own constructor for the Employee class.  You would put its prototype inside the class prototype. The prototype would be Employee();, and its definition would be be placed down with the other Employee method definitions, and look like this: Employee::Employee(){ ... }.

6B.4.5 Public and Private

Finally, we wrap up our questions with the explanation of the access modifiers, public and private. 

The fact that all member data, including private member data, can be accessed inside methods of the same class, is important.  We have seen this already in our class methods above.  The accessor methods had no problem accessing the name or magnitude data of the Galaxy class even though those data were private.  That's because those methods are, themselves, part of the same class.

6B.4.6 In-Line vs. Separate Function Implementation

The two accessor methods getSS() and getName() were defined inside the function prototype.  This is called in-line implementation as compared with the more common way to implement member functions below the prototype (and below main() in my programs).  I only use in-line method implementation for accessors because they are so short and easy.  Any method that requires more than one statement should be implemented the usual way, below the main() method as we did for getOlder() and the other methods.