- •Введение в перегрузку операций
- •Операции, как функции
- •Использование перегруженных операций
- •Ограничения в перегрузке операций
- •Перегрузка операций через дружественные функции
- •Перегрузка операций через дружественные функции
- •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 () {
Number number (6);
std::cout << number;
std::cout << ++number; // вызывается Number::operator++();
std::cout << number++; // вызывается Number::operator++(int);
std::cout << number;
std::cout << --number; // вызывается Number::operator--();
std::cout << number--; // вызывается Number::operator--(int);
std::cout << number;
return 0;
}
Результат выполнения программы:
Здесь есть несколько интересных моментов:
Во-первых, мы отделили версию постфикс от версии префикс использованием целочисленного фиктивного параметра в версии постфикс.
Во-вторых, поскольку фиктивный параметр не используется в реализации самой перегрузки, то мы даже не предоставляем ему имя. Таким образом, компилятор будет рассматривать эту переменную, как простую заглушку (заполнитель места), и даже не будет предупреждать нас о том, что мы объявили переменную, но никогда её не использовали.
В-третьих, операторы версий префикс и постфикс выполняют одно и то же задание: оба увеличивают/уменьшают значение переменной объекта. Разница между ними только в значении, которое они возвращают.
Рассмотрим последний пункт детально. Операторы версии префикс возвращают объект после того, как он был увеличен или уменьшен. В версии постфикс нам нужно возвращать объект до того, как он будет увеличен или уменьшен.
И тут конфуз! Если мы увеличиваем или уменьшаем объект, то мы не можем возвратить его до выполнения инкремента/декремента, так как операция увеличения/уменьшения уже произошла.
С другой стороны, если мы возвращаем объект до выполнения инкремента/декремента, то сама операция увеличения/уменьшения объекта не выполнится.
Решением является использование временного объекта с текущим значением переменной-члена. Тогда можно будет увеличить/уменьшить исходный объект, а временный объект возвратить обратно.
Таким образом, вызывающий код получит копию объекта до того, как фактический объект будет увеличен или уменьшен, и сама операция инкремента/декремента выполнится успешно.
Обратите внимание, это означает, что возврат значения по ссылке невозможен, так как мы не можем возвратить ссылку на локальную переменную (объект), которая будет уничтожена после завершения выполнения тела функции.
Также это означает, что операторы версии постфикс обычно менее эффективны, чем операторы версии префикс, из-за дополнительных расходов ресурсов на создание временного объекта и выполнения возврата по значению вместо возврата по ссылке.
Наконец, мы реализовали перегрузку операторов версии постфикс через уже перегруженные операторы версии префикс. Таким образом, мы сократили дублированный код и упростили внесение изменений в наш класс в будущем (т.е. упростили поддержку кода).
Перегрузка операции индексации []
Рассмотрим перегрузку операции индексации (операции квадратные скобки) в языке С++, ее использование и нюансы, связанные с этим.
При работе с массивами операция индексации ([]) используется для выбора определенных элементов:
myArray[0] = 8; // помещаем значение 8 в первый элемент массива
Рассмотрим следующий класс IntArray, в котором в качестве переменной-члена используется массив:
#include <iostream>
class IntArray {
private:
int m_array [10];
};
int main () {
IntArray array;
// Как получить доступ к элементам m_array?
return 0;
}
Поскольку переменная-член m_array является закрытой, то мы не имеем прямого доступа к m_array через объект array.
Это означает, что мы не можем напрямую получить или установить значения элементов m_array.
Что делать?
Можно использовать геттеры и сеттеры:
class IntArray {
private:
int m_array [10];
public:
void setItem (int index, int value) {m_array[index] = value;}
int getItem (int index) {return m_array[index];}
};
Хотя это работает, но это не очень удобно. Рассмотрим следующий пример:
int main () {
IntArray array;
array.setItem (4, 5);
return 0;
}
Присваиваем ли мы элементу 4 значение 5 или элементу 5 значение 4?
Без просмотра определения метода setItem () этого не понять.
Можно также просто возвращать весь массив (m_array) и использовать оператор [] для доступа к его элементам:
#include <iostream>
class IntArray {
private:
int m_array [10];
public:
int* getArray () {return m_array;}
};
int main () {
IntArray array;
array.getArray () [4] = 5;
return 0;
}
Но можно сделать еще проще, перегрузив операцию индексации.
Операция индексации является одной из операций, перегрузка которой должна выполняться через метод класса.
Функция перегрузки операции [] всегда будет принимать один параметр: значение индекса (элемента массива, к которому требуется доступ).
В нашем случае с IntArray нам нужно, чтобы пользователь просто указал в квадратных скобках индекс для возврата значения элемента по этому индексу:
#include <iostream>
class IntArray {
private:
int m_array [10];
public:
int& operator [] (const int index);
};
int& IntArray::operator[] (const int index) {
return m_array[index];
}
Теперь всякий раз, когда мы будем использовать оператор индексации ([]) с объектом класса IntArray, компилятор будет возвращать соответствующий элемент массива m_array!
Это позволит нам непосредственно как получать, так и присваивать значения элементам m_array:
int main () {
IntArray array;
array[4] = 5; // присваиваем значение
std::cout << array[4]; // выводим значение
return 0;
}
Всё просто. При обработке array [4] компилятор сначала проверяет, есть ли функция перегрузки оператора []. Если есть, то он передает в функцию перегрузки значение внутри квадратных скобок (в данном случае 4) в качестве аргумента.
Обратите внимание, хотя мы можем указать параметр по умолчанию для функции перегрузки оператора [], но в данном случае, если мы, используя [], не укажем внутри скобок значение индекса, то получим ошибку.
