Приведение указателя от базового типа к дочернему типу
Я создаю простой игровой дизайн для моего проекта. У меня есть следующие классы:
class Character
{
public:
virtual void Display();
virtual void SetParameters( char* param, ... );
};
class NonPlayableCharacter : public Character
{
public:
virtual void Display();
virtual void SetParameters( char* paaram, ... );
int GetNPCState();
}
И затем у меня есть куча классов, которые являются производными от Character или NonPlayableCharacter. Я определяю это так:
std::vector<Character*> _allChar;
Моя проблема в том, что в любой момент времени я хотел бы выполнить какую-то операцию с одним из элементов вектора. Таким образом, получая элемент из вектора, я не могу напрямую вызвать метод GetNPCState()
потому что элемент в векторе имеет тип Character *. Так делаем это:
_allChar[0]->GetNPCState();
не работает Поэтому я попытался сделать это с помощью знаменитого dynamic_cast:
NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
test->GetNPCState();
Проблема с этой последней попыткой заключается в том, что GetNPCState()
падает, потому что объект является нулевым, и я точно знаю (через отладку), что _allChar[0] не является нулевым.
7 ответов
Существует несколько типов приведений в C++ (4), из которых 2 представляют интерес:
static_cast
предполагает, что вы знаете, что делаетеdynamic_cast
во время выполнения проверяет, правильно ли вы "угадали"
Примечание: упрощено, как dynamic_cast
также допускает перекрестное приведение и приведение, включающее виртуальные базы.
Есть 3 версии dynamic_cast
Действительно, в зависимости от характера цели:
- если цель является ссылкой (т.е.
dynamic_cast<T&>(u)
), то если проверка не удаласьdynamic_cast
бросаетstd::bad_cast
исключение - если целью является указатель (т.е.
dynamic_cast<T*>(p)
), то если проверки не пройденыdynamic_cast
возвращает нулевой указатель - наконец, в качестве особого случая, если цель
void*
, затемdynamic_cast
вместо этого возвращает адрес полного объекта
В этом случае вы можете:
- переключиться с
dynamic_cast<NonPlayableCharacter*>(_allChar[0])->getNPCState()
вdynamic_cast<NonPlayableCharacter&>(*_allChar[0]).getNPCState()
и пусть исключение распространяется - проверить результат броска (
NonPlayableCharacter* test
здесь) для ненулевой
После
NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
Вы должны проверить, если test
является NULL
, Даже если _allChar[0]
не NULL, dynamic_cast
может вернуться NULL
если объект, на который он указывает, не является NonPlayableCharacter
,
Таким образом, правильная версия будет:
NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
if (test)
{
test->GetNPCState();
}
dynamic_cast
возвращается NULL
если наложение невозможно. Проверьте, что внутри _allChar[0]
, Вы могли бы сделать функцию как getType()
где возвращает идентификатор объекта предопределенного типа, а затем использовать static_cast
:
if (_allChar[0]->getType() == TYPE_NO_PLAYER) {
static_cast<NonPlayableCharacter*>(_allChar[0])->getNpcState();
}
Вероятно, есть лучшее ОО-решение для использования dynamic_cast, но весь смысл использования этого приведения в том, что он вернет нулевой указатель в случае сбоя приведения.
Поэтому проверьте NULL перед вызовом GetNPCState();
NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
if( test != NULL )
{
test->GetNPCState();
}
Чтобы получить дочерний указатель из базового указателя, вы должны использовать dynamic_cast
, Его поведение следующее:
- Если указатель указывает на
Child*
выделено сnew Child
затемdynamic_cast<Child*>
возвращаетChild*
- Если указатель указывает на что-то еще, то
dynamic_cast
возвращаетсяNULL
,
Ваша проблема в том, что либо вы не наделены new
или ваш объект другого типа.
dynamic_cast
возвращается NULL
когда его аргумент не указывает на NonPlayableCharacter
(поэтому первый элемент в массиве, вероятно, указывает на некоторый другой подкласс Character
) - так что вам нужно проверить NULL
после актерского состава. Однако, используя dynamic_cast
может свидетельствовать о проблеме дизайна. Возможно, вы должны вместо этого иметь виртуальный метод на Character
это называется например PerformMainActionInGameLoop()
и переопределяется соответствующим образом в разных подклассах?
Вы должны проверить dynamic_cast
для успеха. Возвращает нулевой указатель при ошибке:
NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
if (test) test->GetNCPState();
Проблема может заключаться в том, что ваш первый элемент не указывает на NonPlayableCharacter
объект.