«Дружественные» операторные функции: отличия, реализация, особенности применения

«Дружественные» операторные функции: отличия, реализация, особенности применения. Перегрузка операторов +, , *, / с помощью «дружественных» операторных функций

Перед изучением данной темы рекомендуется ознакомиться со следующей темой:


Содержание



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

 


Связанные темы