Friend_functions
Friend Functions and Operator Overloading
Section titled “Friend Functions and Operator Overloading”Friend functions and operator overloading are powerful C++ features that allow you to extend the language’s functionality for user-defined types. This chapter covers how to use them effectively.
Friend Functions
Section titled “Friend Functions”A friend function has access to private and protected members of a class, even though it’s not a member of that class.
Declaring Friend Functions
Section titled “Declaring Friend Functions”class Vector {private: double x, y;
public: Vector(double xVal, double yVal) : x(xVal), y(yVal) {}
// Declare function as friend friend double magnitude(const Vector& v);};
// Define friend function (not a member!)double magnitude(const Vector& v) { // Can access private members directly return std::sqrt(v.x * v.x + v.y * v.y);}
int main() { Vector v(3, 4); std::cout << "Magnitude: " << magnitude(v) << std::endl; // 5}Friend Classes
Section titled “Friend Classes”A friend class can access private members:
class Writer {private: std::string name;
public: Writer(const std::string& n) : name(n) {}
friend class Document; // Document can access Writer's private};
class Document {private: Writer author; std::string content;
public: Document(const Writer& w) : author(w) {}
void write(const std::string& text) { content += text; // Can access Writer's private name! std::cout << author.name << " wrote: " << text << "\n"; }};Warning: Use friend classes sparingly - they break encapsulation.
Operator Overloading
Section titled “Operator Overloading”C++ allows you to redefine operators for user-defined types.
Overloadable Operators
Section titled “Overloadable Operators”┌─────────────────────────────────────────────────────────────┐│ Overloadable Operators │├─────────────────────────────────────────────────────────────┤│ Arithmetic → + - * / % ++ -- ││ Comparison → == != < > <= >= ││ Logical → && || ! ││ Bitwise → & | ^ ~ << >> ││ Assignment → = += -= *= /= %= <<= >>= &= |= ││ Member Access → () [] -> ││ Other → new delete , │└─────────────────────────────────────────────────────────────┘Binary Operator Overloading
Section titled “Binary Operator Overloading”class Complex {private: double real, imag;
public: Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// Overload + operator Complex operator+(const Complex& other) const { return Complex(real + other.real, imag + other.imag); }
// Overload - operator Complex operator-(const Complex& other) const { return Complex(real - other.real, imag - other.imag); }
void display() const { std::cout << real << " + " << imag << "i\n"; }};
int main() { Complex c1(3, 4), c2(1, 2); Complex c3 = c1 + c2; // Uses operator+ Complex c4 = c1 - c2; // Uses operator-
c3.display(); // 4 + 6i}Friend Operator Functions
Section titled “Friend Operator Functions”class Complex {private: double real, imag;
public: Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// Declare as friend friend Complex operator+(const Complex& a, const Complex& b);
void display() const { std::cout << real << " + " << imag << "i\n"; }};
// Define as friend (not a member)Complex operator+(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imag + b.imag);}Stream Operators (<< and >>)
Section titled “Stream Operators (<< and >>)”class Point {private: double x, y;
public: Point(double xVal = 0, double yVal = 0) : x(xVal), y(yVal) {}
// Friend for output stream friend std::ostream& operator<<(std::ostream& os, const Point& p) { return os << "(" << p.x << ", " << p.y << ")"; }
// Friend for input stream friend std::istream& operator>>(std::istream& is, Point& p) { return is >> p.x >> p.y; }};
int main() { Point p1(3, 4), p2;
std::cout << p1 << "\n"; // (3, 4)
std::cin >> p2; // Enter: 5 6 std::cout << p2; // (5, 6)}Comparison Operators
Section titled “Comparison Operators”class Rectangle {private: double width, height;
public: Rectangle(double w, double h) : width(w), height(h) {}
double area() const { return width * height; }
// All comparison operators bool operator==(const Rectangle& other) const { return area() == other.area(); }
bool operator!=(const Rectangle& other) const { return !(*this == other); }
bool operator<(const Rectangle& other) const { return area() < other.area(); }
bool operator>(const Rectangle& other) const { return other < *this; }
bool operator<=(const Rectangle& other) const { return !(other < *this); }
bool operator>=(const Rectangle& other) const { return !(*this < other); }};Increment and Decrement Operators
Section titled “Increment and Decrement Operators”class Counter {private: int value;
public: Counter(int v = 0) : value(v) {}
// Prefix ++ Counter& operator++() { ++value; return *this; }
// Postfix ++ (int parameter distinguishes it) Counter operator++(int) { Counter temp = *this; value++; return temp; }
// Prefix -- Counter& operator--() { --value; return *this; }
// Postfix -- Counter operator--(int) { Counter temp = *this; value--; return temp; }
int getValue() const { return value; }};
int main() { Counter c(5);
std::cout << ++c << "\n"; // 6 (prefix) std::cout << c++ << "\n"; // 6, then c becomes 7 std::cout << c << "\n"; // 7}Subscript Operator []
Section titled “Subscript Operator []”class Array {private: int data[10];
public: Array() { for (int i = 0; i < 10; i++) data[i] = 0; }
// Non-const version for modification int& operator[](int index) { return data[index]; }
// Const version for read-only access const int& operator[](int index) const { return data[index]; }};
int main() { Array arr; arr[0] = 100; // Write std::cout << arr[0]; // Read}Function Call Operator ()
Section titled “Function Call Operator ()”class Multiply {public: int operator()(int a, int b) const { return a * b; }};
int main() { Multiply mult; std::cout << mult(5, 3); // 15 - looks like function call}Complete Example: Complex Number Class
Section titled “Complete Example: Complex Number Class”#include <iostream>#include <ostream>#include <istream>#include <cmath>
class Complex {private: double real; double imag;
public: Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// Getters double getReal() const { return real; } double getImag() const { return imag; }
// Arithmetic operators Complex operator+(const Complex& other) const { return Complex(real + other.real, imag + other.imag); }
Complex operator-(const Complex& other) const { return Complex(real - other.real, imag - other.imag); }
Complex operator*(const Complex& other) const { return Complex( real * other.real - imag * other.imag, real * other.imag + imag * other.real ); }
Complex operator/(const Complex& other) const { double denominator = other.real * other.real + other.imag * other.imag; return Complex( (real * other.real + imag * other.imag) / denominator, (imag * other.real - real * other.imag) / denominator ); }
// Compound assignment Complex& operator+=(const Complex& other) { real += other.real; imag += other.imag; return *this; }
// Unary operators Complex operator-() const { return Complex(-real, -imag); }
// Comparison bool operator==(const Complex& other) const { return real == other.real && imag == other.imag; }
bool operator!=(const Complex& other) const { return !(*this == other); }
// Stream operators friend std::ostream& operator<<(std::ostream& os, const Complex& c); friend std::istream& operator>>(std::istream& is, Complex& c);
// Magnitude double magnitude() const { return std::sqrt(real * real + imag * imag); }};
std::ostream& operator<<(std::ostream& os, const Complex& c) { os << c.real; if (c.imag >= 0) os << "+"; os << c.imag << "i"; return os;}
std::istream& operator>>(std::istream& is, Complex& c) { is >> c.real >> c.imag; return is;}
int main() { Complex a(3, 4); Complex b(1, 2);
std::cout << "a = " << a << "\n"; std::cout << "b = " << b << "\n";
Complex c = a + b; std::cout << "a + b = " << c << "\n";
Complex d = a * b; std::cout << "a * b = " << d << "\n";
std::cout << "|a| = " << a.magnitude() << "\n";
return 0;}Best Practices
Section titled “Best Practices”- Prefer member functions when the left operand is your type
- Use friend functions when you need access to private members
- Keep operators consistent with their built-in meaning
- Don’t overload unrelated operators
- Consider return types - return by value for rvalues, by reference for lvalues
Key Takeaways
Section titled “Key Takeaways”- Friend functions have access to private members without being members
- Use friend functions sparingly as they break encapsulation
- Operator overloading allows custom types to use operators
- Stream operators (<<, >>) enable I/O for custom types
- Keep operator semantics consistent with their built-in meaning
Next Steps
Section titled “Next Steps”Now let’s learn about the Standard Template Library (STL).
Next Chapter: 03_stl/14_stl_containers.md - STL Containers Overview