Оператор индексирования [] (subscript operator) позволяет интерпретировать объект как массив или как контейнер других объектов и позволяет выбирать из объекта отдельные элементы. Функция оператора [] должна принимать в качестве аргумента условный индекс, по контрому в объекте-контейнере можно найти нужный элемент. Рассмотрим простейшей пример:
#include <iostream> class Person { public: Person(std::string p_name, unsigned p_age, std::string p_company) { name = p_name; company = p_company; age = p_age; } std::string operator[](unsigned index) const { switch (index) { case 0: return name; case 1: return std::to_string(age); // преобразуем в тип std::string case 2: return company; default: return "Bad Index"; } } private: std::string name; unsigned age; std::string company; }; int main() { Person tom{"Tom", 38, "Microsoft"}; std::cout << tom[0] << "\n" << tom[1] << "\n" << tom[2] << std::endl; }
Здесь класс Person определяет три приватных переменных - name, age и company, которые недоступны из вне. Фактически мы можем рассматривать объект Person как контейнер над этими переменными. И для обращения к ним определяем оператор индексации:
std::string operator[](unsigned index) const { switch (index) { case 0: return name; case 1: return std::to_string(age); case 2: return company; default: return "Bad Index"; } }
Оператор в качестве операнда принимает числовой индекс, в данном случае типа unsigned
(некоторое положительное целое число) и в зависимости от этого индекса
возвращает значение определенной переменной. В данном случае возвращаем значение типа std::string, для этого значение поля age приводим к строке с помощью функции std::to_string
.
Значение какой именно переменной возвращать по тому или иному индексу условно. В данном случае логично (но необязательно) сделать это в порядке объявления переменных в классе. Затем в функции main, используя данный оператор, можно обратиться к определенной переменной через
индекс:
Person tom{"Tom", 38, "Microsoft"}; std::cout << tom[0] << "\n" << tom[1] << "\n" << tom[2] << std::endl;
Консольный вывод:
Tom 38 Microsoft
Причем необязательно использовать именно числовые индексы. Они могут представлять любой тип, например, std::string:
#include <iostream> class Person { public: Person(std::string p_name, unsigned p_age, std::string p_company) { name = p_name; company = p_company; age = p_age; } // строковый индекс std::string operator[](const std::string& prop) const { if(prop=="name") return name; else if(prop== "age") return std::to_string(age); else if(prop=="company") return company; else return "Bad Index"; } private: std::string name; unsigned age{}; std::string company; }; int main() { Person tom{"Tom", 38, "Microsoft"}; std::cout << tom["name"] << ": " << tom["company"] << std::endl; // Tom: Microsoft }
Оператор индексации можно использовать для доступа к элементам из набора, который может быть в объекте. Например, компания имеет набор сотрудников:
#include <iostream> class Company { public: std::string& operator[](unsigned index) { return employees[index]; } private: std::string employees[10]{"Tom", "Sam", "Bob"}; }; int main() { Company company; std::cout << company[0] << std::endl; // Tom company[0] = "Tomas"; std::cout << company[0] << std::endl; // Tomas }
Для простоты класс Company содержит набор сотрудников в виде массива строк. С помощью функции индексации мы можем обратиться к одному из элементов массива employees. Чтобы можно было
изменять возвращенные объекты, оператор возвращает не просто объект std::string
, а именно ссылку std::string&.
Однако главная проблема здесь заключается в организации логики возвращения элемента. В первом случае (в случае с классом Person) необходимо разнотипные значения приводить к единому типу. Во втором случае мы можем столкнуться с ситуацией, что будет передан недействительный индекс. На этот счет есть разные стратегии, но в любом случае подобные ситуации надо иметь в виду при определении оператора.