Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Nonmember operators

In some of the previous examples, the operators may be members or nonmembers, and it doesn’t seem to make much difference. This usually raises the question, “Which should I choose?” In general, if it doesn’t make any difference, they should be members, to emphasize the association between the operator and its class. When the left-hand operand is an object of the current class, it works fine.

This isn’t always the case – sometimes you want the left-hand operand to be an object of some other class. A very common place to see this is when the operators << and >> are overloaded for iostreams:

//: C12:Iosop.cpp
// Iostream operator overloading
// Example of non-member overloaded operators
#include "../require.h"
#include <iostream>
#include <strstream>
#include <cstring>
using namespace std;

class IntArray {
  static const int sz = 5;
  int i[sz];
public:
  IntArray() {
    memset(i, 0, sz* sizeof(*i));
  }
  int& operator[](int x) {
    require(x >= 0 && x < sz,
           "operator[] out of range");
    return i[x];
  }
  friend ostream&
    operator<<(ostream& os,
               const IntArray& ia);
  friend istream&
    operator>>(istream& is, IntArray& ia);
};

ostream& operator<<(ostream& os,
                    const IntArray& ia){
  for(int j = 0; j < ia.sz; j++) {
    os << ia.i[j];
    if(j != ia.sz -1)
      os << ", ";
  }
  os << endl;
  return os;
}

istream& operator>>(istream& is, IntArray& ia){
  for(int j = 0; j < ia.sz; j++)
    is >> ia.i[j];
  return is;
}

int main() {
  istrstream input("47 34 56 92 103");
  IntArray I;
  input >> I;
  I[4] = -1; // Use overloaded operator[]
  cout << I;
} ///:~

This class also contains an overloaded operator[ ] , which returns a reference to a legitimate value in the array. A reference is returned, so the expression

I[4] = -1;

not only looks much more civilized than if pointers were used, it also accomplishes the desired effect.

The overloaded shift operators pass and return by reference, so the actions will affect the external objects. In the function definitions, expressions like

os << ia.i[j];

cause existing overloaded operator functions to be called (that is, those defined in <iostream>). In this case, the function called is ostream& operator<<(ostream&, int) because ia.i[j] resolves to an int.

Once all the actions are performed on the istream or ostream, it is returned so it can be used in a more complicated expression.

The form shown in this example for the inserter and extractor is standard. If you want to create a set for your own class, copy the function signatures and return types and follow the form of the body.

Basic guidelines

Murray[38] suggests these guidelines for choosing between members and nonmembers:

Operator

Recommended use

All unary operators

member

= ( ) [ ] –>

must be member

+= –= /= *= ^=

&= |= %= >>= <<=

member

All other binary operators

nonmember


[38] Rob Murray, C++ Strategies & Tactics , Addison-Wesley, 1993, page 47.

Contents | Prev | Next


Contact: webmaster@codeguru.com
CodeGuru - the website for developers.
[an error occurred while processing this directive]