Оператор равенства должен возвращать значение false при попытке сравнить объекты разных типов. Например, если попытаться сравнивать объект базового класса с объектом производного, оператор == должен возвратить значение false.
С учетом этого наблюдения можно прийти к выводу, что решить данную проблему можно с использованием RTTI. Определим оператор равенства, параметр которого будет ссылкой на тип базового класса. Оператор равенства будет использовать оператор typeid для проверки наличия у операндов одинакового типа. Если тип операндов разный, оператор возвратит значение false. В противном случае он возвратит виртуальную функцию equal(). Каждый класс определит функцию equal() так, чтобы сравнить переменные-члены собственного типа. Эти операторы получают параметр типа Base&, но приводят операнд к собственному типу, прежде чем начать сравнение.
Чтобы сделать концепцию более конкретной, предположим, что рассматриваемые классы выглядят следующим образом:
class Base {
friend bool operator==(const Base&, const Base&);
public:
//
protected:
virtual bool equal(const Base&) const;
//
};
class Derived: public Base {
public:
//
protected:
bool equal(const Base&) const;
//
};
Рассмотрим, как можно было бы определить общий оператор равенства:
bool operator==(const Base &lhs, const Base &rhs) {
//
//
return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}
Этот оператор возвращает значение false, если операнды имеют разный тип. Если они имеют одинаковый тип, оператор делегирует реальную работу по сравнению операндов виртуальной функции equal(). Если операнды являются объектами класса Base, вызывается функция Base::equal(), а если объектами класса Derived — то функция Derived::equal().
equal()Каждый класс иерархии должен иметь собственную версию функции equal(). Начало у функций всех производных классов будет одинаковым: они приводят аргумент к типу собственного класса:
bool Derived::equal(const Base &rhs) const {
//
//
auto r = dynamic_cast
//
//
}
Приведение всегда должно быть успешным, ведь оператор равенства вызывает эти функции только после проверки того, что два операнда имеют одинаковый тип. Однако приведение необходимо, чтобы функция могла обращаться к производным членам правого операнда.
equal() базового классаЭта функция гораздо проще других:
bool Base::equal(const Base &rhs) const {
//
}
Здесь нет никакой необходимости в приведении аргументов перед применением. Оба они, и *this и параметр, являются объектами класса Base, поэтому все доступные для него функции содержатся в классе объекта.
19.2.4. Класс type_info
Точное определение класса type_info зависит от компилятора, но стандарт гарантирует, что класс будет определен в заголовке typeinfo и предоставлять, по крайней мере, те функции, которые перечислены в табл. 19.1.
Этот класс обладает также открытым виртуальным деструктором, поскольку он предназначен для использования в качестве базового класса. Если компилятор позволяет предоставить дополнительную информацию о типе, для этого следует воспользоваться классом, производным от класса type_info.
Таблица 19.1. Функции класса type_info