dynamic_cast
является ответом на вашу проблему.
Описание
Он используется для перехода из базового класса в производный класс, при этом он должен убедиться, что приведение не выполнено, если производный класс не соответствует вашим ожиданиям. Например:
void foo(Shape * p_shape)
{
Triangle * t = dynamic_cast<Triangle *>(p_shape) ;
// if p_shape is a triangle, or derives from triangle,
// then t is non-NULL, and you can use it
}
Дело в том, что t будет ненулевым, даже если p_shape не является в точности треугольником, но все еще наследует треугольник. Например, в случае:
Shape
|
+-- Square
|
+-- Triangle
|
+-- EquilateralTriangle
|
+-- RectangleTriangle
если форма представляет собой треугольник, или равносторонний треугольник, или прямоугольник, то t не будет равен NULL, что намного мощнее, чем ваше первоначальное решение пометить точный тип с помощью константного числа.
Обратите внимание, что для dynamic_cast
для работы с классом в этом классе должен быть хотя бы виртуальный метод (, который обычно имеет место в иерархии наследования деревьев, dynamic_cast
используется в )
Метание dynamic_cast
Вместо использования указателей вы можете использовать ссылки, но со ссылками, поскольку dynamic_cast
не может вернуть «ошибочную ссылку», он выдаст std::bad_cast
, который вы можете поймать при необходимости:
void foo(Shape & p_shape)
{
Triangle & t = dynamic_cast<Triangle &>(p_shape) ;
// if p_shape is a triangle, or derives from triangle,
// then the dynamic_cast succeeds.
// If not, a std::bad_cast is thrown
}
dynamic_cast
злоупотреблять?
Следует отметить, что динамическое приведение без броска на основе указателя может привести к появлению кода, подобного переключателю (, но если вы не можете полагаться на виртуальные методы, вам придется «включать типы»). .. ):
void foo(Shape * p_shape)
{
if(Triangle * t = dynamic_cast<Triangle *>(p_shape))
{
// if p_shape is a triangle, then t is non-NULL,
// and you can use it
}
else if(Square * s = dynamic_cast<Square *>(p_shape))
{
// if p_shape is a square, then t is non-NULL
// and you can use it
}
// etc...
Как и весь код «включения типов», он подвержен ошибкам ( что, если вы забудете обработать тип? ), но иногда этого не избежать, поэтому стоит упомянуть.
(В качестве бонуса любопытства, IIRC, нотация if(type * p = ...)
была сначала добавлена в C ++, чтобы обработать этот случай и сделать код менее многословным ... Если я что-то не пропустил, эта нотация не разрешена в C #)
RTTI
В целом, dynamic_cast
опирается на RTTI (информацию о типе RunTime), которую иногда можно отключить ( на работе, пока несколько лет назад «технические эксперты» не решили, что в этом нет необходимости). и, следовательно, обязательно быть отключенным в наших сборках ... Аааа, "C-with классы экспертов" ... )
Не позволяйте себе быть вовлеченным в войну C против C ++: если вы не работаете в очень стесненной среде (то есть во встроенной разработке), RTTI (как и все другие функции C ++, такие как обработка исключений) должен быть активирован.
Более подробная информация о RTTI: http://www.cplusplus.com/reference/std/typeinfo/
И, возможно, мой вопрос переполнения стека по RTTI вас заинтересует: Возможные примеры C ++ RTTI