С++ динамическое_каст

class CBase { };
class CDerived: public CBase { };

CBase     b; 
CBase*    pb;
CDerived  d; 
CDerived* pd;

pb = dynamic_cast<CBase*>(&d);     // ok: derived-to-base
pd = dynamic_cast<CDerived*>(&b);  // wrong: base-to-derived

Я знаю, что приведение «база к производному» неверно. Но какова внутренняя причина этого? Что такое логическая причина внутри? Я думаю, это трудно вспомнить без дополнительных объяснений. Спасибо!


person user658266    schedule 16.03.2011    source источник
comment
см. здесь Потому что кошка - это животное, но животное не обязательно быть кошкой (это может быть собака).   -  person Martin York    schedule 16.03.2011


Ответы (4)


Для преобразования производного в базовое вам не нужно (и, как правило, не нужно) вообще явно указывать приведение:

CDerived d;
CBase *pb = &d;   // perfectly fine

База производного приведения на самом деле не так уж неправильна, хотя обычно вы предпочитаете ее избегать. Причина этого довольно проста: указатель на базу может указывать на фактический базовый объект или что-либо производное от него. Если вы собираетесь выполнять приведение вниз таким образом, вам, как правило, необходимо проверить, удалось ли преобразование. В конкретном случае, который вы указали, это не удастся, поэтому то, что будет назначено (результат dynamic_cast), будет просто нулевым указателем.

В большинстве случаев вы бы предпочли указать полный интерфейс для объектов класса в базовом классе, поэтому вам редко нужны приведения вниз.

person Jerry Coffin    schedule 16.03.2011

Во-первых, CBase должен быть полиморфным, чтобы вы могли использовать здесь dynamic_cast (то есть он должен иметь хотя бы одну функцию-член virtual). В противном случае вы не сможете использовать dynamic_cast.

Тем не менее, преобразование &b в CDerived* не неправильно: pd будет нулевым указателем.

dynamic_cast имеет полезное свойство, заключающееся в том, что при сбое приведения (то есть, если объект, на который указывает указатель, не относится к целевому типу), он дает нулевой указатель. Это позволяет вам проверить фактический тип объекта. Например:

CBase b;
CDerived d;

CBase* pb = &b;
CBase* pd = &d;

CDerived* xb = dynamic_cast<CDerived*>(pb); // xb is null!
CDerived* xd = dynamic_cast<CDerived*>(pd); // xd points to d!

Ваш код был бы неверным, если бы вы использовали static_cast, поскольку он выполняет приведение без выполнения какой-либо проверки типа во время выполнения, а это означает, что нет способа проверить, успешно ли выполнено приведение. Если вам когда-либо понадобится преобразовать иерархию классов и вы не знаете наверняка, относится ли объект к производному типу, к которому вы пытаетесь привести, вы должны использовать dynamic_cast.

person James McNellis    schedule 16.03.2011

Производный класс может иметь больше «поведений», чем базовый класс. Больше функций-членов, больше данных-членов и т. д. Если вы приведете базовый класс к производному классу, а затем попытаетесь обращаться с ним как с производным классом, вы попытаетесь заставить его делать то, на что он не способен. Потому что вы пытаетесь заставить экземпляр базового класса делать то, что знает только производный класс.

person dappawit    schedule 16.03.2011

pd — это указатель типа CDerived*. Таким образом, pd указывающий объект должен иметь два задействованных подобъекта (т. е. базовый и производный). Но с этим заявлением -

pd = dynamic_cast<CDerived*>(&b);

Здесь pd указывает только на базовый подобъект. Не существует частичного способа указания на подобъекты. Итак, это неправильно.

person Mahesh    schedule 16.03.2011