Accessor and Mutator Functions

download the slides used in this presentation
PowerPoint pptx | Acrobat pdf

Objectives

While engaging with this module, you will...

  1. learn new techniques for working with a class' member variables
  2. be able to decide when and how it is appropriate to provide read or write access to a class' internal data

Introduction

Letís provide some functionality that should natural to fractions. We will want to multiply fractions at some point, so letís define a non-member function that will get the job done for us.

// in the Fractions.cpp file
Fraction mult_fracs(const Fraction & lhs, const Fraction & rhs)
{
    Fraction temp; // a fraction to build locally and then return
    temp.m_Numerator = lhs.m_Numerator * rhs.m_Numerator;
    ****** STOP Ė THIS WONíT COMPILE !! ********

In this code we have attempted a cardinal sin, and it wonít compile. In this last line of code, we have tried to access directly the private member variables of Fraction objects. But we did that in the last lesson, you say! Yes, but those were member functions. The function above is a non-member (or global) function. How do deal with this issue then? There are two approaches.

A note before we go on: You will notice that I named the parameters oddly. lhs and rhs are short for left-hand side and right-hand side, common names for parameters to binary operations. Also, I made them reference parameters so as to avoid the overhead of copying Fraction type objects into the parameters. This is a common practice with user-defined types, which can possibly be large aggregates, thus causing undue overhead.

Accessor and Mutator Functions

Accessor and mutator functions (a.k.a. set and get functions) provide a direct way to change or just access private variables. They must be written with the utmost care because they have to provide the protection of the data that gives meaning to a class in the first place. Remember the central theme of a class: data members are hidden in the private section, and can only be changed by the public member functions which dictate allowable changes. Letís see how we might code up these functions for our example class.

class Fraction
{
  public:
    void readin();
    void print();
    Fraction reciprocal();
    void unreduced(const int multiplier);
    int getNum();                          // accessor or get function
    int getDen();                          // likewise
    void setNumer(const int numer);        // mutator or set function
    bool setDenom(const int denom);        // likewise
  private:
    int m_Numerator;
    int m_Denominator;
};

...and here are the definitions:

// these would go in the Fraction.cpp file
int Fraction::getNumer()
{
    return m_Numerator;
}

int Fraction::getDenom()
{
    return m_Denominator;
}

void Fraction::setNumer(const int numer)
{
    m_Numerator = numer;
    return;
}

bool Fraction::setDenom(const int denom)
{
    bool set = false;

    if (denom != 0) // prohibits setting denominator to zero!
    {
        set = true;
        m_Denominator = denom;
    }

    return set;
}

Now you see that all of these functions are straight forward. The get functions will return the values of the members and the set functions will set the values of the members. Letís analyze how we defined these. The gets are obvious: return the appropriate value. But let me show you how you might want to write such an obvious function. You can ďinlineĒ them into the class definition:

class Fraction
{
  public:
    ...
    int getNumer() {return m_Numerator;}
    int getDenom() {return m_Denominator;}
    ...
}

With quite obvious and simple functions, this acceptable and you are giving away nothing.

As for the set functions, we see that we are able to set the numerator to anything. And that makes sense; a numerator can be any integer, including zero. However, itís different with the value for the denominator. We mustnít let the denominator be set to zero. There are many ways to code this, and you will learn all about exception handling in cs 153, which is probably the best way to handle it. However, not knowing that technique, I have chosen to do the above. I have the setDenom function return a bool to represent whether or not the value of the denominator was set or not. Letís see how we might use these functions now back in the example I started at the beginning of this lesson.

// in the Fractions.cpp file
Fraction mult_fracs(const Fraction & lhs, const Fraction & rhs)
{
    Fraction temp;                                         // a fraction to build locally and then return
    temp.setNumer(lhs.getNumer() * rhs.getNumer());

    if (! (temp.setDenom(lhs.getDenom() * rhs.getDenom()))
    {                                                      //if set returns true, denom is set, all ok
        cout<<"ERROR: denominator is 0 "<<endl;            // if set returns false, denom not set
        exit(2);                                           // and program bombs
    }

    return temp;
}

If the argument to the setDenom() function were to be zero, the program would output a message and then exit. Of course, in this case, some fractionís denominator would have to be zero for this to happen, which we hope could not happen. Well, the way we have the readin() written at this point, it is well possible! We need to rewrite that function:

In general, just because you can write a set or get function for a private member variable, that does not imply that you should. You should only write these functions if they are warranted. Letís consider an example. Suppose you write a program for keeping grades for a course. You have to write a ďstudentĒ class. Member variables include an array of quiz grades, an average, and a letter grade. You will have public member functions that include void calc_grade(). When called, this functionís responsibility is to average the quiz scores, set the average member, and then set the letter grade based on that average. This last exercise might be implemented by the calc_grade() function call a compute_grade() function that sets the letter grade based on average. In any case, is it reasonable to have a public setAverage() function? I think not. It isnít reasonable to be able to set the average arbitrarily to, say, 65 when the quiz grades would indicate an average of 90!

Course Menu
0. Getting Started
0.0. Welcome
1. The Big Picture
2. Programming Fundamentals
3. Operators
4. Branching
5. Loops
6. Advanced Branching
7. Odds and Ends
8. Functions
9. Random Number Generation
10. Multiple Files
11. Arrays
12. Structs
13. Character Arrays
14. File I/O
15. Objects
16. Output Formatting
16.0. Overview
17. Namespaces
18. Enumerations
19. Sample Homework