13.7 – Перегрузка операторов сравнения

Добавлено 18 июля 2021 в 16:41
Глава 13 – Перегрузка операторов  (содержание)

Перегрузка операторов сравнения сравнительно проста, поскольку она следует тем же шаблонам, что мы видели при перегрузке других операторов.

Поскольку все операторы сравнения являются бинарными операторами, которые не изменяют свои левые операнды, мы сделаем наши перегруженные операторы сравнения дружественными функциями.

Вот пример класса Car с перегруженными операторами operator== и operator!=.

#include <iostream>
#include <string>
 
class Car
{
private:
    std::string m_make;
    std::string m_model;
 
public:
    Car(const std::string& make, const std::string& model)
        : m_make{ make }, 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_make == c2.m_make &&
            c1.m_model == c2.m_model);
}
 
bool operator!= (const Car &c1, const Car &c2)
{
    return !(c1 == c2);
}
 
int main()
{
    Car corolla{ "Toyota", "Corolla" };
    Car camry{ "Toyota", "Camry" };
 
    if (corolla == camry)
        std::cout << "a Corolla and Camry are the same.\n";
 
    if (corolla != camry)
        std::cout << "a Corolla and Camry are not the same.\n";
 
    return 0;
}

Код здесь должен быть понятен. Поскольку результат operator!= противоположен operator==, мы определяем operator!= с помощью operator==, что помогает упростить работу, избежать ошибок и сократить объем кода, который нам нужно написать.

А как насчет operator< и operator>? Что для автомобиля объекта Car будет значить быть больше или меньше другого объекта Car? Обычно мы так не думаем об автомобилях. Поскольку результаты operator< и operator> не будут сразу интуитивно понятными, может быть лучше оставить эти операторы неопределенными.

Рекомендация


Не определяйте перегруженные операторы, не имеющие смысла для вашего класса.

Однако есть одно общее исключение из приведенной выше рекомендации. Что, если бы мы хотели отсортировать список объектов Car? В таком случае мы можем захотеть перегрузить операторы сравнения, использующие член (или члены), по которому вы, скорее всего, захотите выполнить сортировку. Например, перегруженный operator< для автомобилей Car может сортировать объекты по марке и модели в алфавитном порядке.

Некоторые из классов-контейнеров в стандартной библиотеке (классы, содержащие наборы объектов других классов) требуют перегруженного operator<, чтобы они могли сохранять элементы в отсортированном порядке.

Вот другой пример с перегруженными operator>, operator<, operator>= и operator<=:

#include <iostream>
 
class Cents
{
private:
    int m_cents;
 
public:
    Cents(int cents)
    : m_cents{ cents }
    {}
 
    friend bool operator> (const Cents &c1, const Cents &c2);
    friend bool operator<= (const Cents &c1, const Cents &c2);
 
    friend bool operator< (const Cents &c1, const Cents &c2);
    friend bool operator>= (const Cents &c1, const Cents &c2);
};
 
bool operator> (const Cents &c1, const Cents &c2)
{
    return c1.m_cents > c2.m_cents;
}
 
bool operator>= (const Cents &c1, const Cents &c2)
{
    return c1.m_cents >= c2.m_cents;
}
 
bool operator< (const Cents &c1, const Cents &c2)
{
    return c1.m_cents < c2.m_cents;
}
 
bool operator<= (const Cents &c1, const Cents &c2)
{
    return c1.m_cents <= c2.m_cents;
}
 
int main()
{
    Cents dime{ 10 };
    Cents nickel{ 5 };
 
    if (nickel > dime)
        std::cout << "a nickel is greater than a dime.\n";
    if (nickel >= dime)
        std::cout << "a nickel is greater than or equal to a dime.\n";
    if (nickel < dime)
        std::cout << "a dime is greater than a nickel.\n";
    if (nickel <= dime)
        std::cout << "a dime is greater than or equal to a nickel.\n";
 
 
    return 0;
}

Это тоже довольно просто.

Обратите внимание, что здесь есть некоторая избыточность. operator> и operator<= являются логическими противоположностями, поэтому одно можно определить, используя другое. operator< и operator>= также являются логическими противоположностями, и одно можно определить, используя другое. В данном случае я решил не делать этого, потому что определения функций настолько просты, а оператор сравнения в имени функции прекрасно сочетается с оператором сравнения в инструкции возврата.

Небольшой тест

Вопрос 1

Для приведенного выше примера Cents перепишите операторы < и <=, используя другие перегруженные операторы.

#include <iostream>
 
class Cents
{
private:
    int m_cents;
 
public:
    Cents(int cents)
    : m_cents{ cents }
    {}
 
    friend bool operator> (const Cents &c1, const Cents &c2);
    friend bool operator>= (const Cents &c1, const Cents &c2);
 
    // friend для operator< и operator<= больше не нужен
};
 
bool operator> (const Cents &c1, const Cents &c2)
{
    return c1.m_cents > c2.m_cents;
}
 
bool operator>= (const Cents &c1, const Cents &c2)
{
    return c1.m_cents >= c2.m_cents;
}
 

bool operator< (const Cents &c1, const Cents &c2)
{
    return !(c1 >= c2);
}
 
// <= и >  логически противоположны,
// поэтому мы можем выполнить > и инвертировать результат
bool operator<= (const Cents &c1, const Cents &c2)
{
    return !(c1 > c2);
}
 
int main()
{
    Cents dime{ 10 };
    Cents nickel{ 5 };
 
    if (nickel > dime)
        std::cout << "a nickel is greater than a dime.\n";
    if (nickel >= dime)
        std::cout << "a nickel is greater than or equal to a dime.\n";
    if (nickel < dime)
        std::cout << "a dime is greater than a nickel.\n";
    if (nickel <= dime)
        std::cout << "a dime is greater than or equal to a nickel.\n";
 
 
    return 0;
}

Вопрос 2

Добавьте перегруженные operator<< и operator< к классу Car из начала урока, чтобы следующая программа компилировалась:

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
 
int main()
{
  std::vector<Car> cars{
    { "Toyota", "Corolla" },
    { "Honda", "Accord" },
    { "Toyota", "Camry" },
    { "Honda", "Civic" }
  };
 
  std::sort(cars.begin(), cars.end()); // требуется перегруженный operator<
 
  for (const auto& car : cars)
    std::cout << car << '\n'; // требуется перегруженный operator<<
 
  return 0;
}

Эта программа должна выдать следующий результат:

(Honda, Accord)
(Honda, Civic)
(Toyota, Camry)
(Toyota, Corolla)

Если вам нужно освежить в памяти std::sort, мы говорили о нем в уроке «10.4 – Сортировка массива с помощью сортировки выбором».

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
 
class Car
{
private:
  std::string m_make;
  std::string m_model;
 
public:
  Car(const std::string& make, const std::string& model)
      : m_make{ make }, 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);
  friend bool operator<(const Car& c1, const Car& c2);
};
 
bool operator==(const Car& c1, const Car& c2)
{
  return (c1.m_make == c2.m_make &&
          c1.m_model == c2.m_model);
}
 
bool operator!=(const Car& c1, const Car& c2)
{
  return !(c1 == c2);
}

std::ostream& operator<<(std::ostream& out, const Car& c)
{
  out << '(' << c.m_make << ", " << c.m_model << ')';
  return out;
}
 
bool operator<(const Car& c1, const Car& c2)
{
  if (c1.m_make == c2.m_make) // Если автомобиль того же производителя...
    return c1.m_model < c2.m_model; // то сравниваем модель
  else
    return c1.m_make < c2.m_make; // иначе сравниваем производителей
}
 
int main()
{
  std::vector<Car> cars{
    { "Toyota", "Corolla" },
    { "Honda", "Accord" },
    { "Toyota", "Camry" },
    { "Honda", "Civic" }
  };
 
  std::sort(cars.begin(), cars.end()); // требуется перегруженный Car::operator<
 
  for (const auto& car : cars)
    std::cout << car << '\n'; // требуется перегруженный Car::operator<<
 
  return 0;
}

Теги

C++ / CppLearnCppДля начинающихОбучениеОператор (программирование)Оператор сравненияПерегрузка (программирование)Перегрузка операторовПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.