Я читал учебники по dynamic_cast
для программы, над которой я работаю, и я не совсем понимаю, как ее использовать.
У меня есть несколько методов, которые будут передаваться в параметрах, которые являются указателями суперкласса, а оттуда различают то, что нужно, чтобы выяснить, какие операции нужно сделать.
Например, суперкласс может быть Value
, а подкласс - RationalNumber
.
Мой план состоял в том, чтобы установить идентификатор значения int
для RationalNumber
, я не знаю, 1, а затем, когда я использую dynamic_cast
, он определит, что объект Value*
самом деле является RationalNumber
, и операция будет выполнена.
Как я могу это реализовать?
Типичный пример:
class Value
{
public:
virtual ~Value();
};
class RationalNumber : public Value
{
...
}
Value *v = new RationalNumber(...)
...
RationalNumber* test = dynamic_cast<RationalNumber*>(v);
if (v)
cout << "v is a rational number" << endl;
else
cout << "v is a not rational number" << endl;
Обратите внимание, что это НЕ является хорошим решением почти во всех случаях, когда у вас есть наследование. Я пишу компилятор, и это первый раз, когда я когда-либо использовал dynamic_cast
- главным образом потому, что есть некоторые довольно общие функции, которые имеют дело с (например) указателями класса ExprAST
и добавляют ВСЕ варианты вариантов, которые все производные классы (31 класс в настоящее время) в составе ExprAST
были бы весьма непрактичными. (И, конечно, вызов, выполняющий expr->isVariableExprAST()
, не намного лучше, чем VariableExprAST* var = dynamic_cast<VariableExprAST*>(expr); if (var)...
- это то же количество кода, более или менее).
Но в большинстве случаев, не зная, с каким типом вы имеете дело, это гораздо лучшее решение.
Более подходящим решением является использование виртуальной функции, которая является общей для всех классов, например:
class Value
{
public:
virtual ~Value();
virtual void SomeMethod() = 0;
};
class RationalNumber : public Value
{
...
virtual void SomeMethod() { ... }
}
class IrrationalNumber : public Value
{
...
virtual void SomeMethod() { ... }
}
Value *v = ...
v->SomeMethod();
В этом случае вам не нужно выполнять ЛЮБЫЕ проверки - вам просто нужно реализовать метод SomeMethod
для всех подтипов.
RationalNumber::checkID{ return ID; } if (v->checkID == 1 && w->checkID == 1){ //execute method } else if ...
?
Оператор dynamic_cast
определяет тип объекта во время выполнения в соответствии со значением указателя V-таблицы объекта (неявное поле члена, которое добавляется к классу объекта, когда у вас есть одна или несколько виртуальных функций в этом классе).
Здесь нечего реализовать, и вы можете просто называть его. Например:
bool isRationalNumber(Value* object)
{
RationalNumber* number = dynamic_cast<RationalNumber*>(object);
if (number == NULL)
return false;
return true;
}
Обратите внимание, что вы не можете использовать его в классе, который не объявляет хотя бы одну виртуальную функцию, так как у этого класса нет V-таблицы.
Кроме того, на некоторых компиляторах вам необходимо включить RTTI в настройках проекта.
И, наконец, небольшое предложение (более личное мнение, поэтому вы можете игнорировать его):
Динамическое приведение в значительной степени в отличие от концепции полиморфизма, в котором обобщается объекты с общими атрибутами в рамках единого базового класса, а затем ссылаться на них с помощью этого универсального базового класса. С динамическим нажатием вы по существу делаете обратное, поскольку вы проверяете конкретный тип объекта, прежде чем принимать решение о том, какое действие нужно предпринять.
Поэтому, пожалуйста, старайтесь избегать использования его, когда это возможно.
Если вы видите, что у вас нет альтернатив, проверьте свой дизайн.
dynamic_cast
является лучшей (или единственной) альтернативой. Как я уже сказал, это скорее личное мнение, что вы должны стараться избегать его использования (и проверить свой дизайн, если видите, что у вас нет альтернатив). Некоторые люди будут утверждать против этого мнения, поэтому вы можете игнорировать его. Использовать сам оператор довольно просто, как показано в ответе выше.
Стандартный способ - dynamic_cast
объект к производному классу, а затем проверить, является ли результат null
или нет. Например (используя вашу терминологию):
void method(SuperClass* object) {
SubClass* subclass = dynamic_cast<SubClass*>(object);
if (subclass) {
// the object is a 'SubClass'
// do stuff...
}
}
Если у вас есть несколько подклассов, вам придется проверять их отдельно. Например:
void method(SuperClass* object) {
SubClass_1* subclass_1 = dynamic_cast<SubClass_1*>(object);
if (subclass_1) {
// the object is a 'SubClass_1'
// do stuff...
return;
}
SubClass_2* subclass_2 = dynamic_cast<SubClass_2*>(object);
if (subclass_2) {
// the object is a 'SubClass_2'
// do stuff...
return;
}
}
dynamic_cast
? Это встроено в язык! Вы просто пытаетесь определить, будет лиdynamic_cast
успешным или неуспешным? Вы даже уверены, что это необходимо? Когда у вас есть виртуальные интерфейсы, это обычно не нужно.