Оператор равенства должен возвращать значение 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