8.7. Определение, является ли класс объекта подклассом другого класса

8.7. Определение, является ли класс объекта подклассом другого класса

Проблема

Имеется два объекта и требуется узнать, имеют ли их классы отношения на уровне базовый класс/производный класс, или они не связаны друг с другом.

Решение

Используйте оператор dynamic_cast, который пытается выполнить преобразование одного типа в другой. Результат скажет, имеется ли связь между классами. Пример 8.7 представляет код, который это делает.

Пример 8.7. Определение отношений классов

#include <iostream>

#include <typeinfo>

using namespace std;

class Base {

public:

 virtual ~Base() {} // Делаем класс полиморфным

};

class Derived : public Base {

public:

 virtual ~Derived() {}

};

int main() {

 Derived d;

 // Запрашиваем тип отношений

 if (dynamic_cast<Base*>(&d)) {

  cout << "Derived является классом, производным от Base" << endl;

 } else {

  cout << "Derived HE является классом, производным от Base" << endl;

 }

}

Обсуждение

Для запроса отношений между двумя типами используйте оператор dynamic_cast. dynamic_cast принимает указатель или ссылку на некий тип и пытается преобразовать его к указателю или ссылке на производный класс, т.е. выполняя преобразование типа вниз по иерархии классов. Если есть Base*, который указывает на объект Derived, то dynamic_cast<Base*>(&d) возвращает указатель типа Derived только в том случае, если d — это объект типа, производного от Base. Если преобразование невозможно (из-за того, что Derived не является подклассом — явным или косвенным — класса Base), то преобразование завершается неудачей и, если в dynamic_cast был передан указатель на производный класс, возвращается NULL. Если в него была передана ссылка, то выбрасывается стандартное исключение bad_cast. Также базовый класс должен наследоваться как public и это наследование не должно быть двусмысленным. Результат говорит о том, является ли один класс наследником другого класса. Вот что я сделал в примере 8.7.

if (dynamic_cast<Base*>(&d)) {

Здесь возвращается нe-NULL-указатель, так как d — это объект класса, производного от Base. Эту возможность можно использовать для определения отношения любых двух классов. Единственным требованием является то, что аргумент объекта должен быть полиморфным типом, что означает, что он должен иметь по крайней мере одну виртуальную функцию. Если это не будет соблюдено, то такой код не скомпилируется. Однако обычно это не вызывает особых проблем, так как иерархия классов без виртуальных функций встречается крайне редко.

Если этот синтаксис кажется вам слишком запутанным, используйте макрос, скрывающий некоторые подробности.

#define IS_DERIVED_FROM(BaseClass, x) (dynamic_cast<baseClass*>(&(x)))

//...

if (IS_DERIVED_FROM(Base, l)){//...

Но помните, что такая информация о типах не бесплатна, так как dynamic_cast должен во время выполнения пройти по иерархии классов и определить, является ли один класс наследником другого, так что не злоупотребляйте этим способом. Кроме того, компиляторы не включают эту информацию по умолчанию, так как RTTI приводит к накладным расходам, и не все используют эту функцию, так что вам может потребоваться включить ее с помощью опции компилятора.

Смотри также

Рецепт 8.6.