C++. Перевантаження інкрементних операторів ++, –. Приклади

Перевантаження інкрементних та декрементних операторів ++, – –. Приклади


Зміст



1. Особливості перевантаження інкрементних та декрементних операторів ++,

Інкрементні та декрементні оператори в мові C++ можуть бути перевантажені. Особливість перевантаження цих операторів полягає в тому, що потрібно перевантажувати як префіксну так і постфіксну форму цих операторів.

Як відомо, у мові C++ операції ++ та – – мають префіксну і постфіксну форми, як показано у прикладі:

++x; // префіксна форма оператора інкременту
x++; // постфіксна форма оператора
--x; // префіксна форма оператора декременту
x--; // постфіксна форма оператора декременту

Згідно синтаксису C++ оператори ++ та потребують одного операнда.

 

2. Як в операторах інкременту ++ та декременту відрізнити реалізації операторних функцій в класі для префіксної та постфіксної форм?

Згідно синтаксису C++ оператори декременту та інкременту потребують одного операнда. Виникає питання: як реалізувати префіксну та постфіксну перевантажену операторну функцію так, щоб вони відрізнялись (не було співпадіння)?

Для того, щоб при реалізації в класі, відрізнити префіксну та постфіксну форми реалізації операторної функції ++ або , потрібно дотримуватись наступних правил:

  • якщо перевантажується префіксна форма оператора ++, то в класі потрібно реалізувати операторну функцію operator++() без параметрів;
  • якщо перевантажується префіксна форма оператора – –, то в класі потрібно реалізувати операторну функцію operator- -() без параметрів;
  • якщо перевантажується постфіксна форма оператора ++, то в класі потрібно реалізувати операторну функцію operator++(int d) з одним цілочисельним параметром. У цьому випадку параметр d не використовується у функції. Він вказується тільки для того, щоб вказати що це саме постфіксна реалізація оператора ++. Ім’я d може бути замінене іншим іменем;
  • якщо перевантажується постфіксна форма оператора – –, то в класі потрібно реалізувати операторну функцію operator–(int d) з одним цілочисельним параметром. Параметр d необхідний для вказання того, що перевантажується постфіксна реалізація оператора – –.

Наприклад.

class SomeClass
{
    // ...

    // операторна функція, яка переванажує префіксний оператор ++ (++X)
    type operator++()
    {
        // тіло операторної функції
        // ...
    }

    // операторна функція, яка переванажує постфіксний оператор ++ (X++)
    type operator++(int d)
    {
        // тіло операторної функції
        // ...
    }

    // операторна функція, яка переванажує префіксний оператор -- (--X)
    type operator--()
    {
        // тіло операторної функції
        // ...
    }

    // операторна функція, яка переванажує постфіксний оператор -- (X--)
    type operator--(int d)
    {
        // тіло операторної функції
        // ...
    }
};

 

3. Приклад перевантаження операторних функцій ++ та – – в класі для одиночних об’єктів

Демонстрється реалізація операторних функцій для класу Integer, який реалізує цлочисельне значення.
Оголошується клас Integer, який містить:

  • внутрішню змінну number цілого типу. Ця змінна зберігає цілочисельне значення;
  • два конструктори, які ініціалізують змінну number;
  • методи доступу до змінної number Get(), Set();
  • дві операторні функції, які перевантажують оператор ++;
  • дві операторні функції, що перевантажують оператор – –.

Реалізація класу Integer наступна:

// перевантаження інкрементних операторів для класу Integer
class Integer
{
private:
    int number; // член даних

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

    // конструктор з 1 параметром
    Integer(int _number) { number = _number; }

    // методи доступу
    void Set(int _number) { number = _number; }
    int Get(void) { return number; }

    // операторна функція, яка перевантажує 
    // префіксну форму оператора ++ для класу Integer
    Integer operator++(void)
    {
        number++;
        return *this; // повернути об'єкт даного класу
    }

    // операторна функція, яка перевантажує
    // постфіксну форму оператора ++ для класу Integer
    Integer operator++(int d) // параметр d не використовується
    {
        number++;
        return *this;
    }

    // перевантаження префіксного --
    Integer operator--(void)
    {
        number--;
        return *this;
    }

    // перевантаження постфіксного --
    Integer operator--(int d) // параметр d не використовується
    {
        number--;
        return *this;
    }
};

Нижче наведено використання класу Integer

// демонстрація використання перевантажених інкрементних
// операторних функцій
Integer d1, d2(5); // оголошення об'єктів класу Integer
Integer d3;
int t1, t2;

// перевірка
t1 = d1.Get(); // t1 = 0
t2 = d2.Get(); // t2 = 5

// виклик перевантажених операторних функцій
++d1; // виклик префіксної операторної функції operator++()
t1 = d1.Get(); // t1 = 1

d2++; // виклик постфіксної операторної функції operator++(int)
t2 = d2.Get(); // t2 = 6

--d1; // префіксний --
d1--; // постфіксний --
t1 = d1.Get(); // t1 = -1

 

4. Приклад перевантаження операторних функцій ++ та – – в класі, які обробляють масиви

Оголошується клас ArrayInt, в якому продемонстровано перевантаження префіксних та постфіксних операторних функцій operator++() та operator- -(), які відповідно збільшують та зменшують на 5 кожен елемент масиву.
Оголошення класу ArrayInt має вигляд:

// клас, що реалізує масив цілих чисел
class ArrayInt
{
private:
    int A[100]; // внутрішня змінна - масив цілих чисел
    int size; // кількість елементів масиву

public:
    // конструктори
    ArrayInt() { size = 0; }

    ArrayInt(int _size)
    {
        if (_size>100) _size=100;
        size = _size;

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

    // методи доступу
    void SetAi(int index, int value)
    {
        if ((index>=0) && (index<=size))
            A[index] = value;
    }

    int GetAi(int index) { return A[index]; }
    int GetSize(void) { return size; }

    // перевантажений оператор ++
    // префіксна форма
    ArrayInt operator++(void)
    {
        for (int i=0; i<size; i++)
            A[i] = A[i] + 5;
        return *this;
    }

    // постфіксна форма оператора ++, параметр t не використовується
    ArrayInt operator++(int t)
    {
        for (int i=0; i<size; i++)
            A[i] = A[i] + 5;
        return *this;
    }

    // префіксна форма оператора --
    ArrayInt operator--(void)
    {
        for (int i=0; i<size; i++)
            A[i] = A[i] - 5;
        return *this;
    }

    // постфіксна форма оператора --, параметр t не використовується
    ArrayInt operator--(int t)
    {
        for (int i=0; i<size; i++)
            A[i] = A[i] - 5;
        return *this;
    }
};

Використання класу ArrayInt може бути таким

// демонстрація використання перевантажених інкрементних
// операторних функцій
ArrayInt AA(10);
int t;

// ініціалізація масиву AA деякими значеннями
for (int i=0; i<AA.GetSize(); i++)
    AA.SetAi(i, i+1);

// перевірка
t = AA.GetAi(2); // t = 3

// виклик перевантажених операторних функцій
AA++;
t = AA.GetAi(2); // t = 3+5 = 8

--AA;
AA--;
t = AA.GetAi(2); // t = 8-5-5 = -2

 

5. Особливості перевантаження операторів інкременту та декременту з допомогою дружніх функцій

Якщо оператори ++ та перевантажуються з допомого дружніх функцій до класу, то потрібно враховувати наступне:

  • дружня до класу функція не отримує покажчика this класу;
  • оскільки дружня функція не отримує покажчика this, то параметр у цю функцію має передаватись за посиланням а не за значенням. Це означає, що перед іменем параметру вказується символ посилання &.

 

6. Відмінності в перевантаженні префіксних та постфіксних операторів інкременту (++) та декременту () з допомогою “дружніх” функцій

Для того, щоб при реалізації дружньої функції до класу, відрізнити префіксну та постфіксну форми реалізації операторної функції ++ або , потрібно дотримуватись наступних правил:

  • якщо перевантажується префіксна форма оператора ++ (++x), то “дружня” операторна функція повинна отримувати один параметр. Цей параметр є посиланням на клас, для якого ця функція реалізована;
  • якщо перевантажується префіксна форма оператора – – (– –x), то “дружня” операторна функція повинна отримувати один параметр, який є посиланням на клас в якому ця функція реалізована;
  • якщо перевантажується постфіксна форма оператора ++ (x++), то “дружня” операторна функція отримує два параметри. Перший параметр є посиланням на дружній клас. Другий параметр є формальним параметром цілого типу, який служить індикатором того, що перевантажується саме постфіксна а не префіксна форма оператора ++. Другий параметр у функції не використовується;
  • якщо перевантажується постфіксна форма оператора – – (x– –), то дружня операторна функція отримує два параметри. Перший параметр є посиланням на дружній клас. Другий параметр не використовується, а служить для визначення того, що саме постфіксна форма оператора – – реалізована.

Наприклад.

// оголошення деякого класу
class SomeClass
{
    // тіло класу
    // ...

    // оголошення дружніх функцій для класу SomeClass
    friend SomeClass operator++(SomeClass & ref); // ++x
    friend SomeClass operator--(SomeClass & ref); // --x
    friend SomeClass operator++(SomeClass & ref, int d); // x++
    friend SomeClass operator--(SomeClass & ref, int d); // x--
};

// оголошення операторної функції,
// яка перевантажує префіксний оператор ++ (++x)
SomeClass operator++(SomeClass & ref)
{
    // ...
}

// оголошення операторної функції,
// яка перевантажує префіксний оператор -- (--x)
SomeClass operator++(SomeClass & ref)
{
    // ...
}

// оголошення операторної функції,
// яка перевантажує постфіксний оператор ++ (x++)
SomeClass operator++(SomeClass & ref, int d)
{
    // ...
}

// оголошення операторної функції,
// яка перевантажує постфіксний оператор -- (x--)
SomeClass operator++(SomeClass & ref, int d)
{
    // ...
}

Як видно з прикладу, “дружня” операторна функція отримує посилання на “дружній” клас.

 

7. Приклади операторних функцій, які перевантажують оператори інкременту та декременту, і які реалізовані як “дружні функції”

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

// клас Integer, що реалізує ціле число
class Integer
{
private:
    int number;

public:
    // конструктори класу
    Integer() { number = 0; }
    Integer(int _number) { number = _number; }

    // методи доступу
    int Get(void) { return number; }
    void Set(int _number) { number = _number; }

    // оголошення "дружніх" операторних функцій
    friend Integer operator++(Integer & ref);
    friend Integer operator--(Integer & ref);
    friend Integer operator++(Integer & ref, int d);
    friend Integer operator--(Integer & ref, int d);
};

// реалізація "дружніх" операторних функцій
Integer operator++(Integer & ref)
{
    ref.number++;  // доступ за посиланням
    return ref;
}

Integer operator--(Integer & ref)
{
    ref.number--;
    return ref;
}

// постфіксна форма - x++
Integer operator++(Integer & ref, int d)
{
    ref.number++;
    return ref;
}

// постфіксна форма - x--
Integer operator--(Integer & ref, int d)
{
    ref.number--;
    return ref;
}

Використання класу Integer в іншому методі може бути, наприклад, таким:

// "дружні" операторні функції, перевантаження операторів ++, --
Integer d1, d2(8);
int t;

// перевірка
t = d1.Get(); // t = 0
t = d2.Get(); // t = 8

// виклик "дружніх" операторних функцій
++d1; // "дружня" префіксна операторна функція operator++()
d2--; // "дружня" постфіксна операторна функція operator--()

t = d1.Get(); // t = 1
t = d2.Get(); // t = 7

Приклад 2. Перевантажуються оператори ++ та — для класу ArrayInt з допомогою “дружніх” операторних функцій. У класі ArrayInt оголошуються внутрішні дані, конструктори, методи доступу та “дружні” до класу операторні функції.
“Дружні” операторні функції перевантажують оператори ++ та – –. “Дружні” операторні функції збільшують або зменшують на 1 значення кожного елементу масиву, який оголошений в класі ArrayInt.

Реалізація класу ArrayInt та “дружніх” операторних функцій наступна

// клас ArrayInt, який реалізує масив цілих чисел
class ArrayInt
{
private:
    int *A; // масив цілих чисел, розмір масиву довільний
    int size; // розмір масиву

public:
    // конструктори класу
    ArrayInt()
    {
        size = 0;
        A = NULL;
    }

    // конструктор з 1 параметром
    ArrayInt(int _size)
    {
        size = _size;
        A = new int[size];
        for (int i=0; i<size; i++)
            A[i] = 0;
    }

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

    void SetSize(int _size)
    {
        // звільнити попередній фрагмент пам'яті
        if (size>0)
            delete[] A;

        // встановити новий розмір масиву
        size = _size;
        A = new int[size];

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

    int GetAi(int index)
    {
        return A[index];
    }

    void SetAi(int index, int value)
    {
        A[index] = value;
    }

    // "дружні" операторні функції ++, --
    friend ArrayInt operator++(ArrayInt & AI);
    friend ArrayInt operator--(ArrayInt & AI);
    friend ArrayInt operator++(ArrayInt & AI, int d);
    friend ArrayInt operator--(ArrayInt & AI, int d);
};

// реалізація "дружніх" операторних функцій ++, --
// ++x
ArrayInt operator++(ArrayInt & AI)
{
    // збільшити кожен елемент масиву на 1
    for (int i=0; i<AI.size; i++)
        AI.A[i] = AI.A[i] + 1; // доступ за посиланням
    return AI;
}

// --x
ArrayInt operator--(ArrayInt & AI)
{
    // зменшити кожен елемент масиву на 1
    for (int i=0; i<AI.size; i++)
        AI.A[i] = AI.A[i] - 1; // доступ за посиланням
    return AI;
}

// x++
ArrayInt operator++(ArrayInt & AI, int d)
{
    // збільшити кожен елемент масиву на 1
    for (int i=0; i<AI.size; i++)
        AI.A[i] = AI.A[i] + 1; // доступ за посиланням
    return AI;
}

// x--
ArrayInt operator--(ArrayInt & AI, int d)
{
    // зменшити кожен елемент масиву на 1
    for (int i=0; i<AI.size; i++)
    AI.A[i] = AI.A[i] - 1; // доступ за посиланням
        return AI;
}

Нижче продемонстровано використання класу ArrayInt та операторних функцій

// "дружні" операторні функції, перевантаження операторів ++, --
ArrayInt A(10);
int t;

// встановлення довільних значень в масиві
for (int i=0; i<A.GetSize(); i++)
    A.SetAi(i, i+1); // A.A[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }

// перевірка
t = A.GetAi(3); // t = 4
t = A.GetAi(5); // t = 6

// використання перевантажених операторів ++, --
A++; // постфіксна функція operator++(ArrayInt, int)
t = A.GetAi(3); // t = 5
t = A.GetAi(5); // t = 7

--A;
--A;
t = A.GetAi(3); // t = 3
t = A.GetAi(5); // t = 5

++A;
++A;
++A;
t = A.GetAi(3); // t = 6

 


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