C++. Перегрузка инкрементных и декрементных операторов ++, —. Примеры

Перегрузка инкрементных и декрементных операторов ++, . Примеры


Содержание



1. Особенности перегрузки инкрементных и декрементных операторов ++, —

Инкрементные и декрементные операторы в языке C++ могут быть перегружены. Особенность перегрузки этих операторов состоит в том, что нужно перегружать как префиксную так и постфиксную форму этих операторов.
Как известно, в языке C++ операции ++ и имеют префиксную и постфиксную формы, как показано в примере:

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

 

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++), то «дружественная» операторная функция получает два параметра. Первый параметр есть ссылкой на дружественный класс. Второй параметр есть формальным параметром целого типа, который служит индикатором того, что перегружается именно постфиксная а не префиксная форма оператора ++. Второй параметр в функции не используется;
  • если перегружается постфиксная форма оператора , то дружественная операторная функция получает два параметра. Первый параметр есть ссылкой на дружественный класс. Второй параметр не используется, а служит для определения того, что именно постфиксная форма оператора реализована.

Например.

// объявление некоторого класса
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

 


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