operator overloading

This and Operator Overloading in C++

Member functions can directly access the class’s member variables for a given object,  including a hidden pointer to itself, called this.

#include 
#include 

class Circle {
private:
	double radius;
	const double cPi = 3.141593;
public:
	double diameter();
	double area();
	Circle& setRadius(double r);
};

Circle& Circle::setRadius(double r) {
	this->radius = r;
	return *this;
}

double Circle::diameter() {
	return this->radius * 2;
}

double Circle::area() {
	return cPi * (radius * radius);
}


int main()
{

	Circle myCircle;

	std::cout << std::fixed << std::showpoint << std::setprecision(2);

	myCircle.setRadius(5);

	std::cout << "Circle's area is " << myCircle.area() << std::endl;

	//associativity of the dot operator is left or right
	std::cout << "Circle's area now is " << myCircle.setRadius(11).area() << std::endl;


    return 0;
}

A friend function of a class is a nonmember function of the class that still has access to the private members of the class. We can make a function be a friend to a class by preceding the function prototype with the reserved word friend. As the function is not a member of the class, we do not include the scope resolution operator or the name of the class in the function’s definition.

Let’s add a friend function to the Circle class.

class Circle {
private:
	double radius;
	const double cPi = 3.141593;
	friend void circleExpand(Circle &cObject);
	friend void circleContract(Circle &cObject);
public:
	double diameter();
	double area();
	Circle& setRadius(double r);
};

//no need for friend keyword in definition
void circleExpand(Circle &cObject) {
	cObject.radius++;
}

void circleContract(Circle &cObject) {
	cObject.radius--;
}

int main()
{
	int i = 1;
	Circle myCircle;

	std::cout << std::fixed << std::showpoint << std::setprecision(2);

	myCircle.setRadius(i);

	while (i++ < 10) {
		std::cout << " The diameter is " << myCircle.diameter() << " and the area is " << myCircle.area() << std::endl;
		circleExpand(myCircle);
	}

    return 0;
}

C++ comes with some operators that are already overloaded. For example, the arithmetic operators are able to handle both integers and floating point numbers, and the addition and subtraction operators can also handle pointers. The right and left shift operators are likewise used to insert and extract data from the input and output streams.

While the only built-in operations on classes are the assignment operator and the member selection operator, it is possible to extend the definitions of most of the operators that can be applied to classes.

In order to write functions to overload operators, we use the reserved operator keyword followed by the operator we want to overload.

C++ operator functions can be either member functions or friend functions of a class. Let’s take a quick look at operator overloading with member functions.

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cmath>

class Point {
private:
	double x;
	double y;
public:
	void setX(double xValue);
	void setY(double yValue);
	double getX() const;
	double getY() const;
	std::string toString() const;
	double getDistance(Point p) const;
	
	//overloaded operators
	Point operator+(const Point& p) const;
	Point operator-(const Point& p) const;
	bool operator==(const Point& p) const;
	
};


void Point::setX(double xValue) {
	this->x = xValue;
}
void Point::setY(double yValue) {
	this->y = yValue;
}

double Point::getX() const {
	return this->x;
}

double Point::getY() const {
	return this->y;
}

std::string Point::toString() const {
	std::ostringstream oStringStream;
	oStringStream << "(" << this->x << ", " << this->y << ")"; 	
       return oStringStream.str(); 
} 

double Point::getDistance(Point p) const {
 	return sqrt(pow(this->x - p.x, 2) + pow(this->y - p.y, 2));
}


//overloaded operator functions------
Point Point::operator+(const Point& p) const {
	Point tempPoint;
	tempPoint.x = this->x + p.x;
	tempPoint.y = this->y + p.y;
	return tempPoint;
}

Point Point::operator-(const Point& p) const {
	Point tempPoint;
	tempPoint.x = this->x - p.x;
	tempPoint.y = this->y - p.y;
	return tempPoint;
}

bool Point::operator==(const Point& p) const {
	return (this->x == p.x && this->y == p.y);
}
//end overloaded operator functions------------------

int main()
{
	Point pointA;
	pointA.setX(42.73);
	pointA.setY(538);

	Point pointB;
	pointB.setX(14);
	pointB.setY(10.11);

	Point pointC;


	if (pointA == pointB) {
		std::cout << "Point A and Point B equal each other." << std::endl;
	}
	else {
		std::cout << "Point A and Point B do not equal each other." << std::endl;
	}

	std::cout << "Point A is at " << pointA.toString() << " and Point B is at " << pointB.toString() << std::endl;
	std::cout << "The distance between Point A and Point B is " << pointA.getDistance(pointB) << std::endl;

	pointC = pointA - pointB;

	std::cout << "Point C is at " << pointC.toString() << std::endl;


    return 0;
}

Operator Overloading

Operator overloading is used to define a set of functions that we attach to the normal, mainly arithmetic operators in the C++ language. As an example, we will define together a complex number class. A complex number, we recall here, consists of two parts, the real part and the imaginary part.

class Complex{
 protected:
 //complex numbers have two parts
 double realPart;
 double imaginaryPart;
};

Next, let’s define several member functions. We will start with the constructors and the accessor methods.

     public:
 //default constructor initializes 
 //number to 0 + 0i
 Complex(void){
 realPart = 0;
 imaginaryPart = 0;
 }
 //assume imaginary part is 0
 Complex(double paramReal, double paramImaginary){
 realPart = paramReal;
 imaginaryPart = paramImaginary;
 }
 //copy constructor
 Complex(const Complex& paramComplex){
 realPart = paramComplex.realPart;
 imaginaryPart = paramComplex.imaginaryPart;
 }

 //accessor functions
 //getters
 //const placed after function name
 //inidcates no values will be modified within function
 
 double Real(void) const{
 return realPart;
 }

 double Imaginary(void) const{
 return imaginaryPart;
 }
 
 //accessor functions
 //setters
 void setReal(double paramReal){
 realPart = paramReal;
 }

 void setImaginary(double paramImaginary){
 imaginaryPart = paramImaginary;
 }

For our first instance of operator overloading, let’s define a << operator for our Complex class. This will enable us to output our complex numbers just like any other data type.

    //overloading output operator
        friend std::ostream& operator<<(std::ostream &outFile, const Complex &Cnumber){
            std::cout << "( " << Cnumber.Real() << " + " << Cnumber.Imaginary() << "i )";
            return outFile;
        }

Note that when overloading the output operator, we must declare it a friend of the class.

Next, let’s implement increment and decrement operators for our class. First, let’s implement the prefix form of the operators, since it is easier.

//increment and decrement operators
        //prefix 
        Complex operator ++(){
            realPart += 1;
            return *this;
        }

        Complex operator --(){
            realPart -= 1;
            return *this;
        }

Next, how about we write a little program to test out what we have written so far?

#include <iostream>
#include "Complex.h"

using namespace std;

int main(void){

    Complex c;

    cout << c << endl;
    
    ++c;

    cout << c << endl;

    cout << --c << endl;

    return 0;
}

Alright, now let’s implement the postfix form of the increment and decrement operators. Since both the prefix and postfix form of the operators use the same function, operator++, we must overload the postfix form to distinguish it from the prefix form. The postfix version takes an integer as an additional argument; the integer itself is meaningless, it’s just a position holder to differentiate the two forms of the operation.

        Complex operator ++(int){
            Complex returnComplex(*this);
            realPart += 1;
            return returnComplex;
        }

        Complex operator --(int){
            Complex returnComplex(*this);
            realPart -= 1;
            return returnComplex;
        }

Next, let’s put in a simple comparison operator, ==, to compare for equality.

    int operator ==(const Complex &CComp){
            if((this->realPart == CComp.realPart) &&
                    (this->imaginaryPart == CComp.imaginaryPart)){
                return 1;
            } else {
                return 0;
            }
        }

And let’s test out our new overloaded operators with a new program.

#include <iostream>
#include <iomanip>
#include "Complex.h"

using namespace std;

int main(void){

    Complex A;
    Complex B(3, (5.0 / 3));
    Complex C(B);

    cout << fixed;
    cout << A << endl;
    cout << B << endl;

    cout << A++ << endl;
    cout << ++A << endl;

    if(C == B){
        cout << C << " == " << B << endl;
    }

    C.setImaginary(.5);

    if((C == B)==false){
        cout << C << " != " << B << endl; 
    }

    return 0;
}

Well, that’s enough for today. We’ll come back to this subject again at another date.