- •Введение в перегрузку операций
- •Операции, как функции
- •Использование перегруженных операций
- •Ограничения в перегрузке операций
- •Перегрузка операций через дружественные функции
- •Перегрузка операций через дружественные функции
- •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 () {
Не всё может быть перегружено через дружественные функции
Операции присваивания (=), индекса ([]), вызова функции (()) и выбора члена (->) перегружаются только через методы класса — это требование языка C++.
Не всё может быть перегружено через методы класса
В разделе перегрузка операций ввода и вывода мы перегружали операцию вывода << для класса Point через дружественную функцию:
#include <iostream>
class Point {
private:
double m_x, m_y, m_z;
public:
Point (double x=0.0, double y=0.0, double z=0.0): m_x(x), m_y(y), m_z(z) { }
friend std::ostream& operator<< (std::ostream &out, const Point &point);
};
std::ostream& operator<< (std::ostream &out, const Point &point) {
// Поскольку operator<<() является другом класса Point, то мы имеем //доступ к закрытым членам Point
out << "Point (" << point.m_x << ", " << point.m_y << ", " << point.m_z << ")";
return out;
}
Int main (){
Point point1(5.0, 6.0, 7.0);
std::cout << point1;
return 0;
}
Однако через метод класса перегрузить операцию << мы не сможем. Почему?
Потому что при перегрузке через метод класса в качестве левого операнда используется текущий объект того класса, для которого перегружается операция! В нашем случае это должен быть объект класса Point.
В этом случае левым операндом является объект типа std::ostream. std::ostream является частью Cтандартной библиотеки C++. То есть объект совсем другого класса.
Мы не можем использовать объект типа std::ostream в качестве левого неявного параметра, на который бы указывал скрытый указатель this, так как указатель this может указывать только на текущий объект текущего класса, члены которого мы можем изменять, поэтому перегрузка оператора << должна осуществляться через дружественную функцию.
Аналогично, хотя мы можем перегрузить операцию сложения operator+ (Dollars, int) через метод класса (как мы делали выше), мы не можем перегрузить operator+(int, Dollars) сложения int с Dollars через метод класса, поскольку int теперь является левым операндом, на который указатель this указывать не может.
Перегрузка операций через методы класса не используется, если левый операнд не является объектом классом (например, int), или это объект другого класса, который мы не можем изменить (например, std::ostream).
Какой способ перегрузки и когда следует использовать?
В большинстве случаев язык C++ позволяет выбирать самостоятельно способ перегрузки операций.
Но при работе с бинарными операциями, которые не изменяют левый операнд (например, operator+()), обычно используется перегрузка через обычную или дружественную функцию, поскольку такая перегрузка работает для всех типов данных параметров (даже если левый операнд не является объектом класса или является объектом класса, который изменить нельзя).
Перегрузка через обычную/дружественную функцию имеет дополнительное преимущество «симметрии», так как все операнды становятся явными параметрами (а не как у перегрузки через метод класса, когда левый операнд становится неявным объектом, на который указывает указатель this).
При работе с бинарными операциями, которые изменяют левый операнд (например, operator+= ()), обычно используется перегрузка через методы класса.
В этих случаях левым операндом всегда является объект класса, на который указывает скрытый указатель this.
Унарные операции обычно тоже перегружаются через методы класса, так как в таком случае параметры не используются вообще.
Поэтому:
- Для операций присваивания (=), индекса ([]), вызова функции (()) или выбора члена (->) используйте перегрузку через методы класса.
- Для унарных операций используйте перегрузку через методы класса.
- Для перегрузки бинарных операций, которые изменяют левый операнд (например, operator+= ()) используйте перегрузку через методы класса, если это возможно.
- Для перегрузки бинарных операций, которые не изменяют левый операнд (например, operator+ ()) используйте перегрузку через обычные/дружественные функции.
Перегрузка унарных операций +, − и логического НЕ
Рассмотрим унарные операции плюс (+), минус (-) и логическое НЕ (!), которые работают с одним операндом.
Так как они применяются только к одному объекту, то их перегрузку следует выполнять через методы класса.
Например, перегрузим унарный оператор минус (-) для класса Dollars:
#include <iostream>
class Dollars {
private:
