Принципы перегрузки операторов сравнения те же, что и в перегрузке других операторов, которые мы рассматривали на предыдущих уроках. Поскольку все операторы сравнения являются бинарными и не изменяют свои левые операнды, то выполнять перегрузку следует через дружественные функции.
Например, перегрузим оператор равенства ==
и оператор неравенства !=
для класса Car:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <iostream> #include <string> class Car { private: std::string m_company; std::string m_model; public: Car(std::string company, std::string model) : m_company(company), m_model(model) { } friend bool operator== (const Car &c1, const Car &c2); friend bool operator!= (const Car &c1, const Car &c2); }; bool operator== (const Car &c1, const Car &c2) { return (c1.m_company == c2.m_company && c1.m_model== c2.m_model); } bool operator!= (const Car &c1, const Car &c2) { return !(c1== c2); } int main() { Car mustang("Ford", "Mustang"); Car logan("Renault", "Logan"); if (mustang == logan) std::cout << "Mustang and Logan are the same.\n"; if (mustang != logan) std::cout << "Mustang and Logan are not the same.\n"; return 0; } |
Всё просто. Поскольку результат выполнения оператора !=
является прямо противоположным результату выполнения оператора ==
, то мы определили оператор !=
, используя уже перегруженный оператор ==
(уменьшив, таким образом, количество кода, сложность и возможность возникновения ошибок).
А как насчет операторов <
и >
? Здесь нужно определиться, чем один объект класса Car может быть лучше другого объекта класса Car, и как это всё выразить в коде. Неочевидно! Поэтому здесь мы и не перегружали операторы <
и >
.
Совет: Не перегружайте операторы, которые являются бесполезными для вашего класса.
Однако, операторы <
и >
можно использовать для сортировки списка автомобилей (объектов класса Car) в алфавитном порядке, используя члены m_company
и m_model
, поэтому всегда рассматривайте разные варианты.
Некоторые классы-контейнеры Стандартной библиотеки C++ требуют перегрузки оператора <
, чтобы они могли сохранять отсортированные элементы.
Перегрузим операторы сравнения >
, <
, >=
и <=
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars) { m_dollars = dollars; } friend bool operator> (const Dollars &d1, const Dollars &d2); friend bool operator<= (const Dollars &d1, const Dollars &d2); friend bool operator< (const Dollars &d1, const Dollars &d2); friend bool operator>= (const Dollars &d1, const Dollars &d2); }; bool operator> (const Dollars &d1, const Dollars &d2) { return d1.m_dollars > d2.m_dollars; } bool operator>= (const Dollars &d1, const Dollars &d2) { return d1.m_dollars >= d2.m_dollars; } bool operator< (const Dollars &d1, const Dollars &d2) { return d1.m_dollars < d2.m_dollars; } bool operator<= (const Dollars &d1, const Dollars &d2) { return d1.m_dollars <= d2.m_dollars; } int main() { Dollars ten(10); Dollars seven(7); if (ten > seven) std::cout << "Ten dollars are greater than seven dollars.\n"; if (ten >= seven) std::cout << "Ten dollars are greater than or equal to seven dollars.\n"; if (ten < seven) std::cout << "Seven dollars are greater than ten dollars.\n"; if (ten <= seven) std::cout << "Seven dollars are greater than or equal to ten dollars.\n"; return 0; } |
Всё просто.
Но, как вы уже могли бы заметить, операторы >
и <=
являются логическими противоположностями, поэтому один из них можно было бы определить через второй. Та же ситуация и с <
и >=
. Но, поскольку определения функций перегрузки столь просты, а операторы в строке объявления функции так хорошо сочетаются с операторами в строке возврата результата, мы решили этого не делать.
Тест
Задание №1
Используя класс Dollars, приведенный выше, перепишите операторы <
и <=
, используя их логические противоположности.
Ответ №1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars) { m_dollars = dollars; } friend bool operator> (const Dollars &d1, const Dollars &d2); friend bool operator<= (const Dollars &d1, const Dollars &d2); friend bool operator< (const Dollars &d1, const Dollars &d2); friend bool operator>= (const Dollars &d1, const Dollars &d2); }; bool operator> (const Dollars &d1, const Dollars &d2) { return d1.m_dollars > d2.m_dollars; } bool operator>= (const Dollars &d1, const Dollars &d2) { return d1.m_dollars >= d2.m_dollars; } // Логической противоположностью оператора < является >=, поэтому мы можем просто инвертировать результат выполнения >= bool operator< (const Dollars &d1, const Dollars &d2) { return !(d1 >= d2); } // Логической противоположностью оператора <= является >, поэтому мы можем просто инвертировать результат выполнения > bool operator<= (const Dollars &d1, const Dollars &d2) { return !(d1 > d2); } int main() { Dollars ten(10); Dollars seven(7); if (ten > seven) std::cout << "Ten dollars are greater than seven dollars.\n"; if (ten >= seven) std::cout << "Ten dollars are greater than or equal to seven dollars.\n"; if (ten < seven) std::cout << "Seven dollars are greater than ten dollars.\n"; if (ten <= seven) std::cout << "Seven dollars are greater than or equal to ten dollars.\n"; return 0; } |
Задание №2
Добавьте перегрузку операторов <<
и <
в класс Car, представленный выше, чтобы следующий фрагмент кода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> #include <string> #include <vector> #include <algorithm> int main() { std::vector<Car> v; v.push_back(Car("Ford", "Mustang")); v.push_back(Car("Renault", "Logan")); v.push_back(Car("Ford", "Ranger")); v.push_back(Car("Renault", "Duster")); std::sort(v.begin(), v.end()); // требуется перегрузка оператора < для класса Car for (auto &car : v) std::cout << car << '\n'; // требуется перегрузка оператора << для класса Car return 0; } |
Выдавал следующий результат:
(Ford, Mustang)
(Ford, Ranger)
(Renault, Duster)
(Renault, Logan)
Ответ №2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#include <iostream> #include <string> #include <vector> #include <algorithm> class Car { private: std::string m_company; std::string m_model; public: Car(std::string company, std::string model) : m_company(company), m_model(model) { } friend bool operator== (const Car &c1, const Car &c2); friend bool operator!= (const Car &c1, const Car &c2); friend std::ostream& operator<< (std::ostream& out, const Car & c) { out << '(' << c.m_company << ", " << c.m_model << ')'; return out; } friend bool operator<(const Car &c1, const Car &c2) { if (c1.m_company < c2.m_company) return true; if (c1.m_company > c2.m_company) return false; if (c1.m_model < c2.m_model) return true; if (c1.m_model > c2.m_model) return false; return false; } }; bool operator== (const Car &c1, const Car &c2) { return (c1.m_company == c2.m_company && c1.m_model == c2.m_model); } bool operator!= (const Car &c1, const Car &c2) { return !(c1 == c2); } int main() { std::vector<Car> v; v.push_back(Car("Ford", "Mustang")); v.push_back(Car("Renault", "Logan")); v.push_back(Car("Ford", "Ranger")); v.push_back(Car("Renault", "Duster")); std::sort(v.begin(), v.end()); // требуется перегрузка оператора < для класса Car for (auto &car : v) std::cout << car << '\n'; // требуется перегрузка оператора << для класса Car return 0; } |
А как перегрузить оператор равенства, чтобы все это работало вот в таком выражении в мэйне?
С примитивными данными такой номер проходит:
А вот со своим типом что-то не могу никак допетрить как перегрузить оператор?
Ответ в явных и неявных приведений типов.
Для фундаментальных типов они есть.
А для своих классов пиши их сам, компилятор не в курсе как твой класс конвертировать в bool.
Разберем как твой пример видит компилятор:
Компилятор его воспринимает следующим образом ((x==y)==z)
x==y возвращает true, так как они равны, затем сравнивается true==z
так как z это фундаментальный тип и он не равен 0 то он конвертируется в true и получается следующее,
компилятор сравнивает true==true и возвращает true, ониж равны, все логично.
Задание 1:
Можно определить один оператор сравнения < или > и через него переопределить все остальные, например:
Для меня отлично работает следующий вариант перегрузки оператора сравнения:
Можно ли где-нить почитать как реализовано сравнение двух переменных типа std::string у оператора "<" ?
Сначала сравниваются нулевые элементы, затем первые, вторые и т.д.?
Или как-то по другому?
С-строки сравниваются независимо от содержимого :))) сравниваются указатели
std::string сравниваются лексикографически.
Или, по простому, "По алфавиту".
Т.е. да, сравниваются первые элементы (коды символов), если они равны, то вторые и т.д. до нахождения различия. Если различий нет, но одна из строк закончилась — значит она и есть "меньше".
ЗЫ "по алфавиту" это очень условно… строчная 'a' будет "больше" заглавной 'A'. Сравнение идет именно по номерам символов
автор 1 коммента прав, используется ascii таблица, где каждому символу char присвоен определенный порядковый номер
Мне иначе вот в голову взбрело написать:
Неправильно.
Пример.
Пара (AAA, ZZZ) должна быть меньше(AAAA, AA).
А с учетом конкатенации AAAZZZ будет больше чем AAAAAA.
Тоже как вариант для оператора сравнения
Мой чемпион
Не чемпион — компании отсортированы в обратном от верного варианте
Не совсем ясно, как функция sort "подхватывает" оператор < для класса Car.
Функция sort является встроенным функционалом заголовочного файла vector, который вы подключаете. Можете детальнее поискать информацию о функции sort в Интернете.
функция std::sort не имеет отношения к векторам. Она в заголовочном файле <algorythm> объявляется и работает со всеми контейнерами для которых определены итераторы, при этом для элементов контейнера должен быть определен оператор "меньше"
Самый часто используемый синтаксис:
std::sort(arr.begin(), arr.end()); — передаем указатель (итератор) на начало контейнера и терминальный указатель (итератор) (тот, который идет ЗА последним)
для этой формы должен быть определен оператор "<" для элементов контейнера
Можно без оператора "<":
std::sort(arr.begin(), arr.end(), checker); — передаем дополнительно функцию-чекер, которая будет определять правила сортировки
аа…погуглила, checker это компаратор.
Т.е. все эти дружественные функции сравнения в уроке — неявно конвертируются в компаратор при sort?