“Дружні” операторні функції: відмінності, реалізація, особливості застосування

“Дружні” операторні функції: відмінності, реалізація, особливості застосування. Перевантаження операторів +, –, *, / з допомогою “дружніх” операторних функцій

Перед вивченням даної теми рекомендовано ознайомитись з темою:


Зміст



1. Відмінності в параметрах між операторною функцією, яка реалізована всередині класу, та “дружньою” операторною функцією

Як відомо, існує два способи перевантаження будь-якого оператору:

  • з допомогою операторної функції, яка реалізована всередині класу;
  • з допомогою операторної функції, яка реалізована як “дружня” (friend) до класу.

Між способами реалізації операторних функцій існує відмінність у кількості параметрів, які отримує операторна функція:

  • для унарних операторів операторна функція всередині класу не отримує параметрів. А “дружня” до класу операторна функція отримує один параметр;
  • для бінарних операторів операторна функція всередині класу отримує один параметр. А “дружня” функція отримує два параметри. У цьому випадку першим параметром “дружньої” функції є лівий операнд, а другим параметром є правий операнд.

Ці відмінності виникають через те, що “дружня” операторна функція не отримує неявного покажчика this. Тому в ній потрібно явно задавати параметри.

 

2. Загальна форма операторної функції, що реалізована за межами класу (“дружня” функція до класу)

Операторна функція може бути реалізована за межами класу. Якщо операторна функція перевантажує унарний оператор, то вона містить один параметр. Якщо операторна функція перевантажує бінарний оператор, то вона містить два параметри.
Загальна форма операторної функції, що є дружньою до класу має вигляд

return_type operator#(arguments_list)
{
    // деякі операції
    // ...
}

де

  • return_type – тип значення, що повертає операторна функція;
  • operator# – ключове слово, що визначає операторну функцію в класі. Символ # визначає оператор мови C++, який перевантажується. Наприклад, якщо перевантажується оператор +, то потрібно вказати operator+;
  • argument_list – список параметрів, які отримує операторна функція. Якщо у “дружній” функції перевантажується бінарний оператор, то argument_list містить два аргументи. Якщо перевантажується унарний оператор, то argument_list містить один аргумент.

У класі ця функція має бути оголошена як “дружня” з ключовим словом friend.

 

3. Приклад перевантаження бінарного оператора ‘–’ у класі для якого реалізована “дружня” операторна функція

За даним прикладом можна розробляти власні операторні функції, які є “дружніми” до заданого класу.
Задано клас Complex, який реалізує комплексне число. У класі оголошуються внутрішні змінні, конструктори, методи доступу та “дружня” функція operator-(). “Дружня” функція operator-(), що реалізована за межами класу, здійснює віднімання комплексних чисел.

// клас Complex
class Complex
{
private:
    float real; // дійсна частина
    float imag; // уявна частина

public:
    // конструктори
    Complex(void)
    {
        real = imag = 0;
    }

    Complex(float _real, float _imag)
    {
        real = _real;
        imag = _imag;
    }

    // методи доступу
    float GetR(void) { return real; }
    float GetI(void) { return imag; }

    void SetRI(float _real, float _imag)
    {
        real = _real;
        imag = _imag;
    }

    // оголошення "дружньої" до класу Complex операторної функції
    friend Complex operator-(Complex c1, Complex c2);
};

// "дружня" до класу Complex операторна функція,
// реалізована за межами класу,
// здійснює віднімання комплексних чисел
Complex operator-(Complex c1, Complex c2)
{
    Complex c; // створити об'єкт класу Complex

    // віднімання комплексних чисел
    c.real = c1.real - c2.real;
    c.imag = c1.imag - c2.imag;

    return c;
}

Використання класу Complex в іншому методі

// використання "дружньої" операторної функції
Complex c1(5,6);
Complex c2(3,-2);
Complex c3; // результат
float a, b;

// перевірка
a = c1.GetR(); // a = 5
b = c1.GetI(); // b = 6

// виклик "дружньої" до класу Complex операторної функції
c3 = c1 - c2;

// результат
a = c3.GetR(); // a = 5-3 = 2
b = c3.GetI(); // b = 6-(-2) = 8

Як видно з вищенаведеного прикладу, “дружня” функція operator-() отримує два параметри. Перший параметр відповідає лівому операнду в бінарному операторі віднімання ‘–’. Другий параметр відповідає правому операнду.

 

4. Приклад перевантаження оператора ‘/’, що обробляє клас, який містить масив дійсних чисел. Операторна функція реалізована як “дружня” функція
// клас - масив типу float
class ArrayFloat
{
private:
    int size;
    float * A; // динамічний розмір масиву

public:
    // конструктори класу
    // конструктор без параметрів
    ArrayFloat()
    {
        size = 0;
        A = NULL;
    }

    // конструктор з двома параметрами
    ArrayFloat(int nsize, float * nA)
    {
        size = nsize;
        A = new float[size];

        for (int i=0; i<nsize; i++)
            A[i] = nA[i];
    }

    // методи доступу
    int GetSize(void) { return size; }

    void SetSize(int nsize)
    {
        if (size>0)
            delete A;

        size = nsize;
        A = new float[size]; // виділити новий фрагмент пам'яті

        // заповнити масив нулями
        for (int i=0; i<size; i++)
            A[i] = 0.0f;
    }

    float GetAi(int index)
    {
        if ((index>=0) && (index<size))
            return A[index];
        else
            return 0;
    }

    void SetAi(int index, float value)
    {
        if ((index>=0) && (index<size))
            A[index] = value;
    }

    // "дружня" операторна функція '/',
    // тільки оголошення, реалізація за межами класу
    friend ArrayFloat operator/(ArrayFloat A1, ArrayFloat A2);
};

// "дружня" до класу ArrayFloat операторна функція operator/()
// функція отримує два параметри A1, A2
ArrayFloat operator/(ArrayFloat A1, ArrayFloat A2)
{
    // створити об'єкт класу ArrayFloat
    ArrayFloat A; // викликається конструктор без параметрів
    int n;

    // взяти мінімумальне значення з розміру двох масивів A1, A2
    n = A1.size;
    if (n>A2.size) n = A2.size;

    // встановити новий розмір масиву A, перерозподіляється пам'ять
    A.SetSize(n);

    // поелементне ділення
    for (int i=0; i<n; i++)
        A.A[i] = A1.A[i] / A2.A[i];

    return A; // повернути новий об'єкт
}

Використання класу ArrayFloat та операторної функції operator-() в іншому методі

// використання "дружньої" операторної функції
float F1[] = { 5.0, 2.0, 2.5, -1.0 }; // додаткові масиви
float F2[] = { 2.5, 1.0, -0.5 };
ArrayFloat AF1(4, F1); // об'єкти класу ArrayFloat
ArrayFloat AF2(3, F2);
ArrayFloat AF3; // результуючий об'єкт
float x, y;

// перевірка 1
x = AF1.GetAi(2); // x = 2.5
y = AF2.GetAi(1); // y = 1.0

// виклик "дружньої" операторної функції operator/()
AF3 = AF1 / AF2; // перевантажений оператор /
x = AF3.GetAi(0); // x = 5.0/2.5 = 2.0
y = AF3.GetAi(2); // y = 2.5/(-0.5) = -5.0

// виклик навпаки AF2/AF1
AF3 = AF2 / AF1;
x = AF3.GetAi(0); // x = 2.5/5.0 = 0.5
y = AF3.GetAi(2); // y = -0.5/2.5 = -0.2

 


Зв’язані теми