Operator Overloading

Dr. Lawlor, CS 202, CS, UAF

There are many different objects that can be added together:
C++ directly supports adding ints and floats.  What's the best way to support the rest?
  1. You could add support into the language for each of them.  The language would get even more hideously complicated, though, and you'd never get all of them.
  2. You could write an "add" method for each of them.  Sadly, "e=a.add(b.add(c.add(d)));" is really a lot tougher to read than "e=a+b+c+d;".
  3. You could make up a way to redefine the "+" operator for your class.
In C++, they chose number 3, which lets you build your own "+" operators for your classes.  The syntax for overloading the addition operator inside a class is usually:
	Classname operator+(const Classname &other) const;
This adds your instance to the "other" instance, and returns the sum in a new instance of your class.  For example:
class Range {
public:
float lo,hi;
Range(float l,float h) {lo=l; hi=h;} // constructor
Range operator+(const Range &r) const { // addition operator (written as a member)
return Range(lo+r.lo, hi+r.hi);
}
};
int foo(void) {
Range a(1.0,1.1), b(10.4,10.7);
Range c=a+b; // woa!
cout<<c.lo<<" ... "<<c.hi<<"\n";
return 0;
}

(Try this in NetRun now!)

Note that you really don't want to modify your own variables inside your operator+, because "c=a+b;" should never change a or b!  Adding "const" after your function parameters guarantees this--it makes this current instance a constant, and modifying your own variables will be a compile error.

You can also overload operators from outside the class, which is written just like a function, but works exactly like above:
class Range {
public:
float lo,hi;
Range(float l,float h) {lo=l; hi=h;} // constructor
};
Range operator+(const Range &l,const Range &r) { // addition operator
return Range(l.lo+r.lo, l.hi+r.hi);
}

(Try this in NetRun now!)

One operator that's very commonly overloaded from outside the class is the "<<" operator, used by cout:
class Range {
public:
float lo,hi;
Range(float l,float h) {lo=l; hi=h;} // constructor
};
Range operator+(const Range &l,const Range &r) { // addition operator
return Range(l.lo+r.lo, l.hi+r.hi);
}
ostream &operator<<(ostream &s,const Range &r) { // stream output operator
s<<r.lo<<" ... "<<r.hi;
return s;
}
int foo(void) {
Range a(1.0,1.1), b(10.4,10.7);
cout<<"The sum is "<<a+b<<"\n"; // add, then output!
return 0;
}

(Try this in NetRun now!)

You can also make these function-type overloaded operators a "friend" of your class, which lets them access the private members.  This is usually cleaner than leaving them outside the class, because they're usually directly related to the class.

A huge range of operators can be overloaded; everything from + and << (above) to +=, ++x (prefix increment), x++ (postfix increment), (int)x (cast-to-int operator int), x(3) (function call operator()), x[3] (indexing operator[]) and more!
Name
Example
Class member operator (for a "class Stuff")
Notes
Addition
c=a+b;
Stuff operator+(const Stuff &b) const {...}
Return a new object
Subtraction
c=a+b; Stuff operator+(const Stuff &b) const {...}
Multiplication
c=a*b;
Stuff operator*(const Stuff &b) const {...}
Division
c=a/b;
Stuff operator/(const Stuff &b) const {...}
Assignment
a=b;
Stuff &operator=(const Stuff &b) {...}
Overwrite this object with b, and return *this.
Addition-assignment
a+=b;
Stuff &operator+=(const Stuff &b) {...}
Modify existing object, and return *this;
Output
cout<<a;
friend ostream &operator<<(ostream &s,const Stuff &a) {...}
Print to s, then return s.
Indexing
c=a[b];
Something &operator[](int b) {...}
Makes a class act like an array.  Return a reference to index b.
Parenthesis
d=a(b,c);
Something operator()(int b,string c) {...}
Makes a class act like a function.  Can take multiple parameters.
Preincrement
++a;
Stuff &operator++(void) {...}
Increment self, and return self.
Postincrement
a++;
Stuff &operator++(int ignored) {...}
Make a copy of self, increment self, return un-incremented copy.
Less-than
if (a<b) ...
bool operator<(const Stuff &b) const {...}
Compare self with b, and return true or false.
Equality
if (a==b) ...
bool operator==(const Stuff &b) const {...}
Less-than-or-equal
if (a<=b) ...
bool operator<=(const Stuff &b) const {...} You'd think C++ would figure these out, but you need to list them explicitly!
Typecast
c=(int)a;
operator int () {...}
Convert a into an integer (or any type!).

Here's an extended example of my "vec3" class, with a huge number of overloaded operators:

/*
A 3D vector: a direction or location in 3D space.
*/
class vec3 {
public:
float x,y,z;

vec3(void) {}//Default consructor
/// Simple 1-value constructors
vec3(int init) {x=y=z=(float)init;}
vec3(float init) {x=y=z=(float)init;}
vec3(double init) {x=y=z=(float)init;}

/// 3-value constructor
vec3(const float Nx,const float Ny,const float Nz) {x=Nx;y=Ny;z=Nz;}
/// float array constructor
vec3(const float *arr) {x=arr[0];y=arr[1];z=arr[2];}

// Copy constructor & assignment operator are by default

/// This lets you typecast a vector to a float array
operator float *() {return (float *)&x;}
operator const float *() const {return (const float *)&x;}

//Basic mathematical operators
int operator==(const vec3 &b) const {return (x==b.x)&&(y==b.y)&&(z==b.z);}
int operator!=(const vec3 &b) const {return (x!=b.x)||(y!=b.y)||(z!=b.z);}
vec3 operator+(const vec3 &b) const {return vec3(x+b.x,y+b.y,z+b.z);}
vec3 operator-(const vec3 &b) const {return vec3(x-b.x,y-b.y,z-b.z);}
vec3 operator*(const float scale) const
{return vec3(x*scale,y*scale,z*scale);}
vec3 operator/(const float &div) const
{float scale=1.0/div;return vec3(x*scale,y*scale,z*scale);}
vec3 operator-(void) const {return vec3(-x,-y,-z);}
void operator+=(const vec3 &b) {x+=b.x;y+=b.y;z+=b.z;}
void operator-=(const vec3 &b) {x-=b.x;y-=b.y;z-=b.z;}
void operator*=(const float scale) {x*=scale;y*=scale;z*=scale;}
void operator/=(const float div) {float scale=1.0/div;x*=scale;y*=scale;z*=scale;}

//Vector-specific 3D operations
/// Return the square of the magnitude of this vector
float magSqr(void) const {return x*x+y*y+z*z;}
/// Return the magnitude (length) of this vector
float mag(void) const {return sqrt(magSqr());}

/// Return the square of the distance to the vector b
float distSqr(const vec3 &b) const
{return (x-b.x)*(x-b.x)+(y-b.y)*(y-b.y)+(z-b.z)*(z-b.z);}
/// Return the distance to the vector b
float dist(const vec3 &b) const {return sqrt(distSqr(b));}

/// Return the dot product of this vector and b
float dot(const vec3 &b) const {return x*b.x+y*b.y+z*b.z;}
/// Return the cosine of the angle between this vector and b
float cosAng(const vec3 &b) const {return dot(b)/(mag()*b.mag());}

/// Return the "direction" (unit vector) of this vector
vec3 dir(void) const {return (*this)/mag();}
/// Return the right-handed cross product of this vector and b
vec3 cross(const vec3 &b) const {
return vec3(y*b.z-z*b.y,z*b.x-x*b.z,x*b.y-y*b.x);
}
/// Make this vector have unit length
void normalize(void) { *this=this->dir();}

/// Return the largest coordinate in this vector
float max(void) const {
float big=x;
if (big<y) big=y;
if (big<z) big=z;
return big;
}
/// Make each of this vector's coordinates at least as big
/// as the given vector's coordinates.
void enlarge(const vec3 &by) {
if (x<by.x) x=by.x;
if (y<by.y) y=by.y;
if (z<by.z) z=by.z;
}
};

/** Utility wrapper functions */
inline float dist(const vec3 &a,const vec3 &b)
{ return a.dist(b); }

inline float dot(const vec3 &a,const vec3 &b)
{ return a.dot(b); }

inline vec3 cross(const vec3 &a,const vec3 &b)
{ return a.cross(b); }

/// Allows "3.0*vec" to compile:
inline vec3 operator*(float scale,const vec3 &v)
{return vec3(v.x*scale,v.y*scale,v.z*scale);}

/// Nice vector print function
inline ostream &operator<<(ostream &s,const vec3 &v) {
s<<"("<<v.x<<", "<<v.y<<", "<<v.z<<")";
return s;
}

// Tiny example of using vectors
int foo(void) {
vec3 C(100.0,110.0,120.0), D(0.1,0.4,0.3); // ray start and direction
for (float t=0.5;t<4.0;t+=1.0) {
vec3 P=C+t*D; // move down ray by t units
cout<<"t="<<t<<" and P="<<P<<"\n";
}
return 0;
}

(Try this in NetRun now!)

The C++ FAQ has a good section on operator overloading.  There's a lot more to talk about regarding the assignment operator and copy constructor, which we'll see next week!