Перегрузка оператора индексирования элементов массива

Перегрузка оператора индексирования элементов массива [ ]


Содержание


1. Какие ограничения накладываются на перегрузку оператора [ ]?

На перегрузку оператора [ ] накладываются следующие ограничения:

  • к этому оператору запрещено применять «дружественные» функции;
  • операторная функция, перегружающая этот оператор, может быть нестатической функцией-членом.

 

2. Перегрузка оператора индексирования элементов массива []. Особенности использования

В языке программирования C++ есть возможность перегружать оператор индексирования элементов массива [ ]. Этот оператор считается унарным, то есть требует одного параметра — индекса массива. Итак, целесообразно перегружать оператор [ ] в классах, где используются массивы.

Если в классе, который реализует массив, перегружен оператор [ ], то объект этого класса можно использовать как обычный массив (с использованием доступа по индексу), что есть очень удобно и естественно.

Например. Пусть задан класс с именем A. В классе перегружен оператор индексирования массива [ ]. Тогда использование экземпляра класса A может быть следующим:

obj[d]

где

  • obj – экземпляр (объект) класса A;
  • d – индекс. Это параметр, который передается операторной функции operator[]() класса A.

В вышеприведенном примере вызов

obj[d]

преобразуется в вызов

obj.operator[](d)

 

3. Варианты реализации операторной функции operator[]() в классе. Общая форма

Для того, чтобы перегрузить оператор индексирования элементов массива [ ], в классе должна быть реализована операторная функция operator[](). Существует 2 варианта реализации операторной функции operator[](). Эти варианты отличаются возвратом значения из операторной функции. Операторная функция получает один параметр, который есть индексом массива.



Вариант 1. Этот вариант применяется, когда оператор [ ] должен быть использован только в правой части оператора присваивания. То есть значение элемента массива в классе не изменяется.
В этом случае операторная функция operator[]() возвращает значение некоторого типа. Общая форма функции при ее реализации в классе

class ClassName
{

    // ...

    // операторная функция возвращает значение типа type
    type operator[](int d)
    {
        // ...
    }
};

После этого можно использовать экземпляр класса ClassName следующим образом:

x = obj[3];

где

  • x – некоторая переменная типа type;
  • obj – экземпляр (объект) класса ClassName.

Вариант 2. Этот вариант применяется в случае, когда нужно чтобы оператор [] размещался в левой и правой части оператора присваивания. Наличие оператора индексирования [] в левой части оператора присваивания означает, что в объекте класса можно изменять значения элементов массива по индексу.

Отличие от предыдущего варианта (Вариант 1) состоит в том, что функция возвращает ссылку & на тип. Общая форма объявления операторной функции следующая

class ClassName
{

    // ...

    // операторная функция возвращает ссылку (&) на тип type
    type& operator[](int d)
    {
        // ...
    }
};

После такого объявления можно использовать экземпляр класса ClassName следующим образом:

// использование оператора [] для объекта класса
x = obj[3]; // в правой части оператора присваивания
obj[5] = y; // в левой части оператора присваивания

где

  • x – некоторая переменная типа type;
  • obj – экземпляр (объект) класса ClassName.

 

4. Примеры перегрузки оператора индексирования элементов массива []

Пример 1. Пример перегрузки оператора [] в классе. Класс реализует массив чисел типа int. Максимальное количество элементов массива равно 10. Операторная функция класса возвращает ссылку на тип int. Это означает, что оператор [] можно использовать в левой части оператора присваивания.
В классе Array10 объявляются:

  • A – внутренний массив из 10 целых чисел;
  • n – количество элементов в массиве;
  • конструкторы класса;
  • метод GetN(). Этот метод предназначен для получения значения n;
  • методы SetAi() и GetAi() для доступа к элементам массива A;
  • операторная функция operator[](). Эта функция перегружает оператор доступа к элементу массива по его индексу.

Текст программы следующий:

#include <iostream>
using namespace std;

// класс, реализующий массив из 10 целых чисел
class Array10
{
private:
    int A[10]; // массив
    int n; // количество элементов в массиве

public:
    // конструкторы класса
    // конструктор без параметров
    Array10()
    {
        n = 0;
    }

    // конструктор с 1 параметром
    Array10(int _n)
    {
        if ((_n >= 0) && (_n <= 10))
        {
            n = _n;
            for (int i = 0; i < n; i++)
                A[i] = 0; // обнулить массив
        }
        else
            n = 0;
    }

    // методы доступа к элементам массива
    // прочитать количество элементов массива
    int GetN()
    {
        return n;
    }

    // записать в элемент массива значение
    void SetAi(int index, int item)
    {
        if ((index>=0)&&(index<10))
            A[index] = item;
    }

    // взять значение по индексу
    // внутренний метод
    int GetAi(int index)
    {
        return A[index];
    }

    // перегрузка оператора [] получения элемента массива
    // операторная функция, реализованная внутри класса
    int& operator[](int index)
    {
        return A[index];
    }
};

void main()
{
    // оператор индексирования массива []
    Array10 A1(10);

    // сформировать A1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
    for (int i = 1; i <= 10; i++)
        A1[i - 1] = i; // вызов операторной функции operator[]() класса Array10

    // проверка
    int t;

    t = A1[3]; // вызов операторной функции, t = 4

    // Оператор [] в левой части оператора присваивания
    A1[5] = 205;
    t = A1.GetAi(5); // t = 205

    cout << "t = " << t << endl;
}

Как видно из примера, операторная функция возвращает ссылку на тип int

// операторная функция - возвращает int&
int& operator[](int index)
{
    return A[index];
}

Это значит, что оператор [ ] можно использовать в левой части оператора присваивания

...

// Оператор [] в левой части оператора присваивания
A1[5] = 205;

...

Если операторную функцию реализовать так, что она возвращает значение int (а не ссылку int&)

// операторная функция - возвращает int
int operator[](int index)
{
    return A[index];
}

то использовать оператор [ ] в левой части оператора присваивания будет запрещено

...

// Оператор [] в левой части оператора присваивание
A1[5] = 205; // ошибка, запрещено!

t = A1[3]; // так работает, можно

...

Пример 2. Перегрузка оператора индексирования элементов массива [] для массива структур типа BOOK.

В примере объявляются:

  • структура типа BOOK, содержащая информацию о книге;
  • класс ArrayBooks, реализующий динамический массив книг типа BOOK.

В классе ArrayBooks реализованы:

  • внутренняя переменная-указатель на тип BOOK;
  • внутренняя переменная n, которая определяет количество книг в массиве;
  • два конструктора;
  • метод GetN(), возвращающий количество книг n в массиве;
  • операторная функция operator[](). С помощью этой функции осуществляется доступ к заданному элементу массива. Функция перегружает оператор доступа по индексу [].

Текст программы, созданной по шаблону Console Application следующий:

#include <iostream>
#include <stdio.h>
using namespace std;

// структура, описывающая книгу
struct BOOK
{
    string title; // название книги
    int year; // год издания
    float price; // стоимость книги
};

// класс, реализующий массив книг типа BOOK
// содержит перегруженную операторную функцию operator[]()
class ArrayBooks
{
private:
    BOOK * B; // пам'ять для массива выделяется динамически
    int n; // количество книг

public:
    // конструкторы класса
    ArrayBooks()
    {
        n = 0;
        B = NULL;
    }

    ArrayBooks(int _n)
    {
        n = _n;

        // выделить память для указателей на BOOK
        B = (BOOK*) new BOOK[n];

        // заполнить каждую книгу пустыми значениями
        for (int i=0; i<n; i++)
        {
            B[i].title = "";
            B[i].price = 0.00;
            B[i].year = 0;
        }
    }

    // деструктор
    ~ArrayBooks()
    {
        // освободить память, выделенную под массив структур
        if (n>0)
            delete[] B;
    }

    // метод, возвращающий значение n
    int GetN(void) { return n; }

    // операторная функция, которая перегружает оператор индексирования []
    // функция возвращает BOOK&, для того чтобы оператор [] можно было использовать
    // в левой части оператора присваивания
    BOOK& operator[](int index)
    {
        if ((index>=0)&&(index<n))
          return B[index];
        else
        {
            // вернуть пустую структуру
            BOOK BB;
            BB.title = "";
            BB.price = 0.0;
            BB.year = 0;
            return BB;
        }
    }
};

void main()
{
    // демонстрация перегрузки оператора []
    ArrayBooks AB(3); // создать объект класса ArrayBooks, в массиве 3 элемента

    // вызов операторной функции operator[](), сформировать значения
    // книга - 1
    AB[0].title = "This is a first book";
    AB[0].price = 99.99;
    AB[0].year = 2999;

    // книга - 2
    AB[1].title = "This is a second book";
    AB[1].price = 125.95;
    AB[1].year = 2020;

    // книга - 3
    AB[2].title = "Third book";
    AB[2].price = 777.77;
    AB[2].year = 2000;

    // проверка
    string title;
    double price;
    int year;

    title = AB[1].title; // title = "This is a second book"
    price = AB[1].price; // price = 125.95
    year = AB[1].year; // year = 2020

    // вывести на экран
    cout << "Title = " << title.c_str() << ::endl;
    cout << "Price = " << price << ::endl;
    cout << "Year = " << year << ::endl;
}

Результат выполнения программы

Title = This is a second book
Price = 125.95
Year = 2020

 

5. Какие требования к параметру операторной функции operator[]()?

Операторная функция, которая перегружает оператор индексирования элементов массива, получает параметр, который есть индексом в массиве. Тип параметра не обязательно может быть int. Допускается другой тип параметра в индексе массива (например, char).

 

6. Пример класса, который содержит операторную функцию operator[](), индекс которой есть тип char

Этот пример демонстрирует утверждение, что тип индекса в операторной функции может быть не только int.
В примере реализован класс CharIndex, содержащий массив из 26 элементов. В классе объявляется операторная функция operator[](), которая в качестве индекса получает значение типа char.

В программе подсчитывается количество вхождений символов латинского алфавита ‘а’..’z’ в заданном тексте.

Текст программы, созданной по шаблону Console Application, следующий

#include <iostream>
using namespace std;

// доступ по индексу в операторной функции - класс CharIndex
class CharIndex
{
private:
    int A[26]; // массив количества вхождений символа в тексте

public:
    CharIndex()
    {
        for (int i=0; i<26; i++)
            A[i] = 0;
    }

    // операторная функция - доступ по типу char
    int& operator[](char c)
    {
        int position; // позиция в массиве A
        position = (int)c - int('a');
        return A[position];
    }
};

void main(void)
{
    // демонстрация утверждения,
    // что тип индекса не обязательно должен быть int
    CharIndex cI; // объект класса CharIndex
    string text; // заданный текст
    int i;
    char sym;

    // задан некоторый текст
    text = "C++ is the best language in the world!";

    // вычислить количество вхождений символов 'a'..'z' в тексте
    for (i = 0; i<text.length(); i++)
        cI[text[i]]++; // вызов операторной функции

    // вывод, выводятся только ненулевые значения
    for (i=0; i<26; i++)
    {
        sym = 'a' + i; // взять индекс
        if (cI[sym]>0)
            cout << sym << " = " << cI[sym] << ::endl;
    }
}

Результат выполнения программы

a = 2
b = 1
d = 1
e = 4
g = 2
h = 2
i = 2
l = 2
n = 2
o = 1
r = 1
s = 2
t = 3
u = 1
w = 1

 


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