Previous Table of Contents Next


7.3.2. Member Functions

On the surface, a member function is simple enough: It is a class member that happens to have function type. So, for example, we might change our Point class slightly:

   struct Point {
       int xval, yval;
       int x() { return xval; }
       int y() { return yval; }
       void x(int newx) { xval = newx; }
       void y(int newy) { yval = newy; }
   };

Here, we have changed the names of our x and y members to xval and yval, and added overloaded member functions x and y that get and set the x- and y-values. Then we can write code like this:

   Point p;
   p.x(42);          // p.xval is now 42
   p.y(37);          // p.yval is now 37
   int n = p.x();    // n is now 42

The ability to write such code relies on two subtleties of C++. We can see the first one by examining, say, the body of one of the x member functions of Point:

   int x() { return xval; }

To what object does the name xval refer? To call this particular x function, we had to write an expression similar to p.x(), so the answer is the xval th is a member of p. That is, when we call a member function, we call it as a member of some object, just as we do when we refer to a data member.

The second subtlety stems from the first: Within the body of a member function, we can refer to other members of that same object without having to say what object we mean. We have just seen the reason: Calling a member function requires that it be a member of a particular object, so once we have said what object we mean, we know what object will contain the other members to which we might be referring.

Here is another way to look at it. When we call p.x(), we are apparently not passing any parameters to x. But appearances deceive, because while x is running, it knows that it is doing so on behalf of the object p. In effect, we can think of p as a hidden argument to x even though p does not actually appear in the argument list.

Indeed, we can carry this line of reasoning a little further, by noting that within the body of x, not only is the identity of p implicitly known, but it is made explicitly available to us through a “variable” called this. The idea is that while a member function is executing, the keyword this is the address of the object on behalf of which it is executing. We could, therefore, have written our Point structure as follows, without changing its behavior at all:

   struct Point {
       int xval, yval;
       int x() { return this->xval; }
       int y() { return this->yval; }
       void x(int newx) { this->xval = newx; }
       void y(int newy) { this->yval = newy; }
   };

We did not have to write this-> explicitly because within the body of a member function, uttering the name of another member of the same class is equivalent to prefixing the name of that member by this->.

There is one more subtlety we must mention, which has to do with the type of this. At first it would seem that, within a member function of class point, the type of this would be Point*. A little reflection, though, should convince us that it should be Point* const, to prevent its value from being changed while a member function is running. That is, while a member function is executing, it is not allowed to say that it should suddenly start working on some other object instead.

But what about this case?

   const Point q = p; q.x(36);

We have said that q is not supposed to change after we create it, and then we ask to change q.xval to be 36. What should happen here?

The answer is that this program will not compile. While q.x is executing, the variable this, with type Point* const, would have to hold the address of q. But the address of q has type const Point*, and we cannot convert from that type to Point* const.

What we need is a way to say that it is permitted to call q.x(), because doing so will not change the value of q. Moreover, we must be able to do so without making a similar promise for q.x(36), because that will change the value of q.

The way we make such a promise is to change the definition of Point yet again:

   struct Point {
       int xval, yval;
       int x() const { return xval; }     // const added
       int y() const { return yval; }     // const added
       void x(int newx) { xval = newx; }
       void y(int newy) { yval = newy; }
   };


Previous Table of Contents Next