TB 5.2 - Friend Functions

#ece244

Note

If you're reading this and wondering why the naming convention changed, it's because this is around the time when I stopped showing up to classes. Yay!

It's also around the time I discovered that the textbook is actually a better resource for my learning than Salma's lectures. No shade to Salma (she wrote the textbook after all)

issue! operator overloading when LHS is not an object of same class

Consider the following:

Complex z(3, 4);
cout << z;

This exhibits the following issues:

  1. ostream (the class that cout is a part of) does not take type Complex as a data type, and we cannot redefine it to do so because ostream is part of the C++ standard library.
  2. There is no operator<< function we can call because the data type that it would be called on is not an object that we can redefine. For the above example, this means that cout cannot be a member of the function Complex.
    We also can't just have operator<< be a non-member function (meaning it is defined outside of the class), taking ostream and Complex objects as parameters, because this prevents us from accessing the private members of the Complex class.

The solution, then, is to use a friend function of the Complex class.


friend functions

To alleviate the issue we encountered above, we can use a friend function.

Definition: Friend functions

A Friend Function is a non-member function that:

  1. has access to private members of the class in which it is declared a friend, as if they were public members
  2. allows left operand (LHS) to be a different type
example

We can declare operator<< as a friend function of the object Complex. This will have the following syntax:

class Complex {
	// ...
	friend ostream& operator<<(ostream& os, const Complex& rhs);
}

Or more generally:

class Complex {
  // ...
  friend <<return type>> operator<<(<<parameter list>>);
};

Notes:

example of a friend function

take the Complex class from L13 and L14. If we want to define a operator<< function, this is how we would do it:

#include <iostream>
using namespace std;

class Complex {
	private:
		double real;
		double img;
	public:
		Complex() { // the constructor
			real = 0;
			img = 0;
		}
		Complex(double r, double i) {
		// the constructor if we pass in the right params
			real = r;
			img = i;
		}
		// overloading the << operator
		friend ostream& operator<<(ostream& os, const Complex& rhs);
		// return reference to ostream object
		// pass in ostream reference (not const bc we are modifying it)
		// also pass in unmodifiable Complex reference to get data members
};

ostream& operator<<(ostream& os, const Complex& rhs) {
    os << "(" << rhs.real << ", " << rhs.img << ")" << endl; 
    return os; // return the same stream object
}

int main(void) {
	Complex z(3.0, 4.0);
	cout << "z is " << z << endl;
	return 0;
}

Notes: