Week 6:

Operator Overloading and Friends Functions

 

 

Overview

 

C++ classes can be feature-rich, complex and powerful

We have function overloading by means of difference of function's signatures

We can overload operators as well

The same principal follows: "one kind of operator can have many forms"

 

 

Operator Overloading

 

Actually, some operators in C++ are already overloaded

The operator '*'. If it applies to an address, it yields the value stored at that address (de-reference)

If applies to numbers, it multiplies the numbers

The operator '<<' will print out number, char or string

When apply to C++ objects, we want to have the following:

// Instead of using 
for (int i = 0; i < 20; i++)
	evening[i] = carmen[i] + richard[i];	// Add element by element

// We can have a class defined (with internal data structure an array

couple = carmen + richard;

 

 

Syntax

 

To overload an operator, we have to use a special function call "operator"

operatorop(argument-list)

Where op is the symbol for the operator being overloaded, op must be a valid operator in C++

E.g. operator+ function would overload the + operator

E.g
If richard, carmen, couple are objects of Peoples class, then we can write

couple = richard + carmen;

Or recognizing the operands as belonging to the Peoples class, we can rewrite it to:

couple = richard.operator+(carmen);

 

 

Const Member Function

 

const Stock land = Stock("IBM")

land.show();	// This will fail, because show does not guarantee that 
          	// it won't modify the invoking object. And show has no
		// parameter, so there is no trick we can play in the parameter list

So, C++ allows that we use the const keyword after the function declaration

Example:

void show() const;	// promises not to change invoking object
void stock::show() const	// promises not to change invoking object

We called these functions: const member functions

 

 

A Vector Class

 

Our first example: A vector, used in engineering and physic a lot, is a quantity having both a magnitude (size) and a direction

Example:

// vector0.h -- vector class before operator overloading
#ifndef _VECTOR0_H_
#define _VECTOR0_H_
class Vector
{
private:
	double x;				// horizontal value
	double y;				// vertical value
	double mag;				// length of vector
	double ang;				// direction of vector
	void set_mag(void);
	void set_ang(void);
public:
	Vector(void);
	Vector(double h, double v);		// set x, y values
	~Vector(void);
	void set_by_polar(double m, double a);
	double xval() const {return x;}	// report x value
	double yval() const {return y;}	// report y value
	double magval() const {return mag;}	// report magnitude
	double angval() const {return ang;}	// report angle
	void show_polar(void) const;	// show polar values
	void show_vector(void) const ;	// show rectangular values
};
#endif

 

 

Example: Vector Class

 

// Let's Overload the + operator by adding the following

Vector operator+(const Vector & b) const;		// prototype

Vector Vector::operator+(const Vector & b) const	// definition
{
	double sx, sy;
	sx = x + b.x;		// x component of sum
	sy = y + b.y;		// y component of sum 
	Vector sum = Vector(sx, sy);
	return sum;
}
We pass the parameter by reference to save memory and performance time
The function should not alter the value of the vectors it is adding, we
declare the argument as a const type and declare the function a const method
// When using, we can use either
Vector q = move1.operator+(move2);	// function call syntax
Vector q = move1 + move2;		// alternative syntax

More example in the demo/lab

 

 

Friends and Operator Overloading

 

C++ controls access to the private portions of a class object (by the use of private and public sessions)

Usually only public class methods serve as the only access

Might be too rigid to fit particular programming problems ... So we have 'friends'

Friends can be:

 

 

Quick Case Study

 

By making a function a friend to a class, you allow the function the same access privileges that a member function of the class has

Often overloading a binary operator (a operator with two arguments) for a class generates a need for friends

For example:

// Continue with our vector example, let's overload the * operator
// So, if you much a vector by 3, all the component within the vector will
// get multiple by 3
Vector operator*(double n) const;	// prototype
// Multiplies invoking vector by n
Vector Vector::operator*(double n) const	// definition
{
	double mx, my;
	mx = n * x;
	my = n * y;
	Vector mult = Vector(mx, my);
	return mult;
}

 

// The code works fine as follow:
Vector v1 = myvect * 2.0;		// valid code

// Or in alternative form
Vector v1 = myvect.operator*(2.0);

The above function require that operator*() be a member function of the Vector class AND the function takes a type double arugment

But ... what about this:

Vector v1 = 2.0 * myvect;		// Not support
// Because
Vector v1 = 2.0.operator*(myvect);	// Is nonsense

Actually, we really need a function that looks like this:

Vector operator*(double n, const Vector &a);

So, 2.0 * myvector will be

operator*(2.0, myvector);

We can do this by making friends :)

 

 

Making Friends with Your Class

 

A friend function is a nonmember function that is allowed access to an object's private section

Use the friend keyword

Example:

friend Vector operator*(double n, const Vector & a);		// Nonmember friend
Vector operator*(double n, const Vector & a)
{
	return a * n;
}
// Now, this is supported
Vector v1 = 2.0 * myvect;

In General, if you want to overload an operator for a class and you want to use the operator with a nonclass term as the first operand, you have to use a friend function to reverse the operand order.

 

 

Good and Bad

 

Both Friend and Operator Overloading are "Good and Bad" features in C++

Operator Overloading makes the object operations much compact and relatively easier to code

However, once operators are overloaded, code maintance becomes more expensive. For example, + now can be applied to multiple different objects and all the implementation is internal ... it might be a nightmare for someone to change or update the existing code.

Friend, on the other hand, breaks the donut concept we have. Data encapsulation can be broken by friend functions.

Good OO design should avoid friend to ensure data encapsulation

However, friend will be your good friend when you are doing performance enhancement or quick bug fix.

Note: both of these features are not present in Java

 

 

Demo and Lab

 

Sequence.hpp (class definition)

Sequence.cpp (class implementation)

week6.cpp (source code)

week6.out (output)