Приведение указателя от базового типа к дочернему типу

Я создаю простой игровой дизайн для моего проекта. У меня есть следующие классы:

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 объект.

Другие вопросы по тегам