Кратко:
- Методы-члены: перегрузка операторов в виде методов-членов класса.
- Дружественные функции: перегрузка операторов с использованием дружественных функций.
- Свободные функции: перегрузка операторов с использованием свободных функций.
Методы-члены
- Перегружаемые операторы определяются как методы класса.
- Используются для операторов, где левый операнд должен быть экземпляром класса.
class Complex {
private:
    double real, imag;
 
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
 
    // Перегрузка оператора + как метода-члена
    Complex operator+(const Complex &c) const {
        return Complex(real + c.real, imag + c.imag);
    }
 
    // Перегрузка оператора == как метода-члена
    bool operator==(const Complex &c) const {
        return real == c.real && imag == c.imag;
    }
};Дружественные функции
Дружественные функции: позволяют внешним функциям доступ к приватным и защищённым членам класса. Объявляются внутри класса с ключевым словом friend.
Полный пример программы с использованием дружественной функции
Вот пример программы, в которой используется дружественная функция для перегрузки оператора << для класса Complex.
#include <iostream>
 
class Complex {
private:
    double real;
    double imag;
 
public:
    // Конструктор
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
 
    // Дружественная функция для перегрузки оператора <<
    friend std::ostream& operator<<(std::ostream &out, const Complex &c);
};
 
// Определение дружественной функции для перегрузки оператора <<
std::ostream& operator<<(std::ostream &out, const Complex &c) {
    out << "(" << c.real << ", " << c.imag << ")";
    return out;
}
 
int main() {
    // Создание объектов класса Complex
    Complex c1(1.2, 3.4);
    Complex c2(5.6, 7.8);
 
    // Использование перегруженного оператора <<
    std::cout << "Complex number 1: " << c1 << std::endl;
    std::cout << "Complex number 2: " << c2 << std::endl;
 
    return 0;
}Пояснение
- 
Класс Complex:- Имеет приватные члены realиimagдля хранения действительной и мнимой частей комплексного числа.
- Конструктор инициализирует члены, значения по умолчанию — 0.
 
- Имеет приватные члены 
- 
Дружественная функция: - friend std::ostream& operator<<(std::ostream &out, const Complex &c);объявляется внутри класса- Complex, чтобы предоставить дружественной функции доступ к приватным членам- realи- imag.
 
- 
Определение дружественной функции: - std::ostream& operator<<(std::ostream &out, const Complex &c)определяет, как должно выводиться комплексное число. Функция получает объект- Complexи поток вывода- std::ostream, а затем выводит строку в формате- (real, imag).
 
- 
Функция main:- Создаёт два объекта Complex.
- Использует перегруженный оператор <<для вывода объектовComplex.
 
- Создаёт два объекта 
Свободные функции
- Используются для перегрузки операторов, когда можно обойтись без доступа к приватным членам или если требуется функциональность, независимая от конкретного класса.
class Matrix {
private:
    int data[2][2];
 
public:
    Matrix(int a = 0, int b = 0, int c = 0, int d = 0) {
        data[0][0] = a; data[0][1] = b;
        data[1][0] = c; data[1][1] = d;
    }
 
    // Метод для доступа к данным
    int get(int row, int col) const {
        return data[row][col];
    }
};
 
// Свободная функция для перегрузки оператора *
Matrix operator*(const Matrix &m1, const Matrix &m2) {
    return Matrix(
        m1.get(0, 0) * m2.get(0, 0) + m1.get(0, 1) * m2.get(1, 0),
        m1.get(0, 0) * m2.get(0, 1) + m1.get(0, 1) * m2.get(1, 1),
        m1.get(1, 0) * m2.get(0, 0) + m1.get(1, 1) * m2.get(1, 0),
        m1.get(1, 0) * m2.get(0, 1) + m1.get(1, 1) * m2.get(1, 1)
    );
}Заключение
- Методы-члены: удобны для операторов, где левый операнд является экземпляром класса.
- Дружественные функции: используются для доступа к приватным членам и симметричных операций.
- Свободные функции: хороши для независимых операций и когда доступ к приватным членам не требуется.