Source Code. Projects. Nerd Stuff. Art Stuff.

Lesson 9: Defining Operations on Classes

Defining Operations on Classes

ADT Operations, part 1 (Section 8.1)

  • Friend function – a function which is not part of a class, but is allowed access to the private members of the class. The function’s prototype is put inside the class definition, prefaced by the keyword “friend”. The function header and body are then written as usual, without the keyword “friend”, the class name with scope resolution operator, or anything else that indicates its privileges. Example:
    1
    2
    3
    4
    5
    6
       // Inside class definition:
    friend bool equal(Distance d1, Distance d2);
    ... // Outside class definition:
    bool equal(Distance d1, Distance d2)
    {
    ... // function code
  • Some compilers don’t implement friends correctly. Microsoft Visual C++ is the most widely used example of this. So if you have problems using “friend” with this or another compiler, you may be able to download a patch to fix it, or you can just compile on hills using aCC -AA
  • Parameter-Passing Mechanism Review:
    • Call-by-Value – the default parameter-passing mechanism for all datatypes except for arrays. In this mechanism, the argument and the parameter occupy separate memory spaces, and the value of the argument is copied to give the parameter its initial value. This way, changes to the parameter’s value are local to the function, and don’t affect the argument. Example:
      1
         void displayValue(int x);
    • Call-by-Reference – specified by an & following the datatype. In this mechanism, the argument and the parameter use the same memory space. So changes to the parameter are changes to the argument as well. Example:
      1
         void getValues(int& x, int& y);
    • Array Parameters – the default parameter-passing mechanism for arrays. In this mechanism, the starting address of the array is passed by value, but the actual contents of the array aren’t duplicated (because that could take too much time and memory). The array referred to by the parameter is the same one in the same location as that referred to by the argument. So changes to the parameter array’s are changes to the argument array’s values. Very similar behavior to call-by-reference. Example:
      1
         void getValues(int values[]);
    • Constant Array Parameters – the only way to get call-by-value – like behavior with array parameters. The const keyword is used to specify that the contents of the array parameter cannot be modified by the function. So any changes to the array in the function will generate compiler errors. Example:
      1
         void displayValues(const int values[]);
  • Your understanding of call-by-reference and the const keyword should help you understand their uses with classes:
  • Because a class can have many member variables, it can take some extra time and RAM to copy all of them when that class is passed as a call-by-value parameter. Call-by-reference parameters, however, don’t require the data to be copied, so they are faster. But they open up the risk of changes to argument values. So we use the const keyword with call-by-reference to pass large parameters quickly without allowing the argument value to be changed. This can be done with any parameter, whether or not it’s a class. Example:
    1
       void displayMonth(const Month& value);
  • Every member function has an implicit parameter: the object on which the function is called. This object can be modified by default. But if we want to guarantee that a member function won’t modify the object on which it is called, we can use the const keyword. It simply appears at the end of the function header. Example:
    1
    2
    3
    4
    5
       void display() const;
    ...
    void Month::display() const
    {
    ...
  • Example Program: Distance Class using friends, const, call-by- reference, etc.

Overloading Operators (Section 8.2 and Appendix 9)

  • Operator Overloading allows you to define the actions of standard operators with objects of your class, e.g. What does > mean with regard to rectangles?
  • To implement operator overloading, write a special function using the operator keyword. This function can be a member function or a friend function. Section 8.2 shows you how to overload operators as friend functions, and Appendix 9 shows you how to overload operators as member functions. When a binary operator is overloaded as a member function, the left-side argument is the object on which the function is called, and the right-side argument is the object named as a parameter. For example, in the main function we might have the following code:
    1
    2
      Rectangle rect1, rect2;
    if (rect1 > rect2) cout << "rect1 is bigger";

    which could call either of the following functions (we should define only one of the following):

    1
    2
      bool Rectangle::operator>(Rectangle r2) // member function header
    friend bool operator > (Rectangle r1, Rectangle r2); // friend function prototype

    In the member function, rect1 is the implicit parameter and rect2 is passed into the function as r2. In the friend function, there is no implicit parameter, but rect1 is passed into the function as r1 and rect2 as r2.

  • The following four operators cannot be overloaded
    1
        ::    .    .*    ?:
  • Example Program: Box class with > operator defined
  • In addition to binary operators (such as + and *) unary operators can also be overloaded (such as ++ and – )
  • Automatic Type Conversion with Operators – when appropriate, a constructor will automatically be used to convert from a simple type to an object type for an operation. For example, if you define an operator to work with two objects, but you use the operator between an object and an int, an object will automatically be constructed from that int if such a constructor (with a single int parameter) has been defined. An example of this is at the end of the main function in distanceOverload.cpp, which is mentioned at the bottom of this page as well.

    The Mystery of << and >> Revealed!

  • The insertion operator << and the extraction operator >> are binary operators which can be defined to work with any class. That’s how they are made to work for with the istream and ostream classes.
  • If you examine the line:
    1
      cout &lt;&lt; x;

    you can see that << has two operands: the cout object, and the variable x. So we’ll use the left operand (cout) to output the right operand (x).

  • Now we must decide what << returns. In a simple statement like above, nothing seems to be returned. But what about this line:
    1
      cout &lt;&lt; x &lt;&lt; y;

    It may help to think about it as the operators are evaluated:

    1
      (cout &lt;&lt; x) &lt;&lt; y;

    Then you may see that in order for this to work, a single << operation should return the cout object it uses, so that object can be used again.

  • In order to really make the above mechanism work, however, we have to return the cout object in a special way. We need to be able to access it again to do more output, so we need a form of the object that can be modified. Normal return values aren’t stored in a way that allows them to be modified. (e.g. You can’t write
    1
     (x + y) = z;

    ) So instead of returning cout in the normal way, we return a reference to cout. This is done using the &

  • So now we have all the information we need to write the operator function. As a friend function its prototype would be:
    1
      friend ostream&amp; operator &lt;&lt; (ostream&amp; outStream, const Rectangle&amp; r);
  • Overloading an input stream is done in much the same way.
  • Example Program: Distance Class with lots of operators defined on it