- •Введение в перегрузку операций
- •Операции, как функции
- •Использование перегруженных операций
- •Ограничения в перегрузке операций
- •Перегрузка операций через дружественные функции
- •Перегрузка операций через дружественные функции
- •Int main () {
- •I have 16 dollars.
- •Int m_dollars;
- •Int getDollars () const {return m_dollars;}
- •Int main () {
- •Дружественные функции могут быть определены внутри класса
- •Int m_dollars;
- •Int getDollars () const {return m_dollars;}
- •Int main () {
- •Перегрузка операций с операндами разных типов
- •Int m_dollars;
- •Int getDollars () {return m_dollars;}
- •Int main () {
- •Values (int min, int max) {
- •Int getMin () {return m_min;}
- •Int getMax () {return m_max;}
- •Int main () {
- •Int main () {
- •Int main () {
- •Перегрузка операций через обычные функции
- •Int m_dollars;
- •Int getDollars () const {return m_dollars;}
- •Int main () {
- •Int m_dollars;
- •Int getDollars () const {return m_dollars;}
- •Int main () {
- •Int m_dollars;
- •Int getDollars () const {return m_dollars;}
- •Int main () {
- •Перегрузка операций ввода и вывода
- •Void print () {
- •Int main () {
- •Int main () {
- •Int main () {
- •Int main () {
- •Int main () {
- •Int m_numerator;
- •Int m_denominator;
- •Void reduce () {
- •Void print () {
- •Перегрузка операций через методы классов
- •Int m_dollars;
- •Int getDollars () { return m_dollars; }
- •Int main () {
- •Int m_dollars;
- •Int getDollars () {return m_dollars;}
- •Int main (){
- •Не всё может быть перегружено через дружественные функции
- •Не всё может быть перегружено через методы класса
- •Int main (){
- •Какой способ перегрузки и когда следует использовать?
- •Int m_dollars;
- •Int getDollars () const {return m_dollars;}
- •Int main () {
- •If (!isHappy)
- •Int main () {
- •If (!something)
- •Перегрузка операций сравнения
- •Int main () {
- •Int m_dollars;
- •Int main () {
- •Перегрузка операторов инкремента и декремента
- •Int m_number;
- •Int main () {
- •Перегрузка операций инкремента и декремента версии постфикс
- •Int m_number;
- •Int main () {
- •Перегрузка операции индексации []
- •Почему оператор индексации [] использует возврат по ссылке?
- •Использование оператора индексации с константными объектами класса
- •Int& operator[] (const int index);
- •Int main (){
- •IntArray array;
- •Проверка ошибок
- •Int m_array[10];
- •Int& operator [] (const int index);
- •Int& IntArray::operator[] (const int index) {
- •Указатели на объекты и перегруженный оператор []
- •Передаваемый аргумент не обязательно должен быть целым числом
- •Void operator [] (std::string index);
- •Void Something::operator [] (std::string index){
- •Int main () {
- •Перегрузка операции ()
- •Int main () {
- •Void operator() ();
- •Void Matrix::operator()() {
- •Int main() {
- •Перегрузка операций преобразования типов данных
- •Int m_dollars;
- •Int getDollars () { return m_dollars; }
- •Int main () {
Int main () {
Matrix matrix;
matrix (2, 3) = 3.6;
std::cout << matrix(2, 3);
return 0;
}
Результат выполнения программы
3.6
Выполним перегрузку оператора () еще раз, но уже без использования каких-либо параметров:
#include <iostream>
#include <cassert> // для assert()
class Matrix {
private:
double data [5][5];
public:
Matrix () {
// Присваиваем всем элементам массива значение 0.0
for (int row=0; row < 5; ++row)
for (int col=0; col < 5; ++col)
data[row][col] = 0.0;
}
double& operator() (int row, int col);
const double& operator() (int row, int col) const;
Void operator() ();
};
double& Matrix::operator()(int row, int col) {
assert(col >= 0 && col < 5);
assert(row >= 0 && row < 5);
return data[row][col];
}
const double& Matrix::operator()(int row, int col) const {
assert(col >= 0 && col < 5);
assert(row >= 0 && row < 5);
return data[row][col];
}
Void Matrix::operator()() {
// Сбрасываем значения всех элементов массива на 0.0
for (int row=0; row < 5; ++row)
for (int col=0; col < 5; ++col)
data[row][col] = 0.0;
}
Int main() {
Matrix matrix;
matrix(2, 3) = 3.6;
matrix(); // выполняем сброс
std::cout << matrix(2, 3);
return 0;
}
Результат выполнения программы:
0
Поскольку операция () является очень гибкой, то может возникнуть соблазн использовать её для самых разных целей.
Однако это настоятельно не рекомендуется делать, поскольку операция () не является очень информативным и зачастую может быть не понятно, что он делает.
В примере, приведенном выше, функцию очистки массива лучше было бы записать в виде метода clear() или erase(), поскольку matrix.erase() выглядит намного информативнее, нежели matrix().
Функторы в C++
Перегрузка операции () используется в реализации функторов (или «функциональных объектов») — это классы, которые работают как функции.
Преимущество функтора над обычной функцией заключается в том, что функторы могут хранить данные в переменных-членах (поскольку они сами являются классами). Вот пример использования простого функтора:
#include <iostream>
class Accumulator {
private:
int m_counter = 0;
public:
Accumulator () { }
int operator () (int i) { return (m_counter += i); }
};
int main() {
Accumulator accum;
std::cout << accum(30) << std::endl; // выведется 30
std::cout << accum(40) << std::endl; // выведется 70
return 0;
}
Обратите внимание, использование класса Accumulator выглядит так же, как и вызов обычной функции, но наш объект класса Accumulator может хранить значение, которое увеличивается.
Вы можете спросить: «Зачем использовать класс, если всё можно реализовать и через обычную функцию со статической локальной переменной?». Можно сделать и через static, но, поскольку функции представлены только одним глобальным экземпляром (т.е. нельзя создать несколько объектов функции), использовать эту функцию мы можем только для выполнения чего-то одного за раз. С помощью функторов мы можем создать любое количество отдельных функциональных объектов, которые нам нужны, и использовать их одновременно.
Итак, перегрузка оператора () с двумя параметрами используется для получения доступа к двумерным массивам или для возврата подмножеств одномерного массива (два параметра будут конкретизировать условия отбора элементов подмножества).
Всё остальное лучше реализовать через отдельные методы с более информативными названиями, нежели через перегрузку оператора ().
Перегрузка оператора () также часто используется при создании функторов. Хотя функторы, которые мы использовали выше, являются довольно простыми и понятными, но обычно они используются в более продвинутых/сложных темах программирования и заслуживают большего понимания.
Тест
Напишите класс, переменной-членом которого является строка. Перегрузите оператор () для возврата подстроки, которая начинается с индекса, указанного в значении первого параметра. Второй параметр должен указывать требуемую длину подстроки.
Подсказки:
Вы можете использовать индекс массива [] для доступа к отдельным символам строки.
Вы можете использовать оператор += для добавления чего-либо к строке.
Следующий фрагмент кода:
int main () {
Mystring string ("Hello, world!");
std::cout << string (7, 6); // начинаем с 7-го символа (индекса) и //возвращаем следующие 6 символов
return 0;
}
Должен выдавать следующий результат:
world!
