if (typeid(*bp) == typeid(*dp)) {
//
}
//
if (typeid(*bp) == typeid(Derived)) {
//
}
В первом операторе if
сравниваются динамические типы объектов, на которые указывают указатели bp
и dp
. Если оба указателя указывают на тот же тип, то условие истинно. Точно так же второй оператор if
истин, если указатель bp
в настоящее время указывает на объект класса Derived
.
Обратите внимание: операндами оператора typeid
являются проверяемые объекты (*bp
), а не указатели (bp
).
//
if (typeid(bp) == typeid(Derived)) {
//
}
Это условие сравнивает тип Base*
с типом Derived
. Хотя указатель указывает на объект типа класса, обладающего виртуальными функциями, сам указатель не является объектом типа класса. Тип Base*
может быть вычислен и вычисляется во время компиляции. Этот тип не совпадает с типом Derived
, поэтому условие всегда будет ложно, bp
.
typeid
к указателю (в отличие от объекта, на который указывает указатель) возвращает статический тип времени компиляции указателя.
Оператор typeid
требует, чтобы проверка во время выполнения определила, обрабатывается ли выражение. Компилятор обрабатывает выражение, только если у типа есть виртуальные функции. Если у типа нет никаких виртуальных функций, то оператор typeid
возвращает статический тип выражения; статический тип известен компилятору и без вычисления выражения.
Если динамический тип выражения может отличаться от статического, то выражение следует вычислить (во время выполнения), чтобы определить результирующий тип. Это различие имеет значение при выполнении оператора typeid(*p)
. Если p
указывает на тип без виртуальных функций, то указатель p
не обязан быть допустимым указателем. В противном случае выражение *p
вычисляется во время выполнения, тогда указатель p
обязан быть допустимым. Если указатель p
пуст, то выражение typeid(*p)
передаст исключение bad_typeid
.
Упражнение 19.6. Напишите выражение для динамического приведения указателя на тип Query_base
к указателю на тип AndQuery
(см. раздел 15.9.1). Проверьте приведение, используя объект класса AndQuery
и класса другого запроса. Выведите сообщение, подтверждающее работоспособность приведения, и убедитесь, что вывод соответствует ожиданиям.
Упражнение 19.7. Напишите то же приведение, но приведите объект класса Query_base
к ссылке на тип AndQuery
. Повторите проверку и удостоверьтесь в правильности работы приведения.
Упражнение 19.8. Напишите выражение typeid
, чтобы убедиться, указывают ли два указателя на класс Query_base
на тот же тип. Затем проверьте, не является ли этот тип классом AndQuery
.
19.2.3. Использование RTTI
В качестве примера случая, когда может пригодиться RTTI, рассмотрим иерархию класса, для которого желательно реализовать оператор равенства (см. раздел 14.3.1). Два объекта равны, если у них тот же тип и то же значение для заданного набора переменных-членов. Каждый производный тип может добавлять собственные данные, которые придется включать в набор проверяемых на равенство.
Казалось бы, эту проблему можно решить, определив набор виртуальных функций, которые проверяют равенство на каждом уровне иерархии. Сделав оператор равенства виртуальным, можно было бы определить одну функцию, которая работает со ссылкой на базовый класс. Этот оператор мог бы передать свою работу виртуальной функции equal()
, которая и осуществляла бы все необходимые действия.
К сожалению, виртуальные функции не очень хороши для решения этой задачи. Параметры виртуальной функции должны иметь одинаковые типы и в базовом, и в производных классах (см. раздел 15.3). Если бы пришлось определить виртуальную функцию equal()
, то ее параметр был бы ссылкой на базовый класс. Если параметр является ссылкой на базовый класс, то функция equal()
сможет использовать только члены из базового класса. Функция equal()
никак не могла бы сравнить члены, определенные в производном классе.