2013-07-01 2 views
7

Учитывая базовый класс gameObject и производный класс animatedGameObject, я думал, что может быть хорошо хранить все их экземпляры в std::vector. Если объявлено, что вектор GameObjects является базовым типом gameObject*, производные экземпляры объектов требуют кастинга.Существуют ли риски производительности для использования static_cast для работы с вектором смешанных (базовых и производных) объектов? (aka «это эта немая идея?»)

Пример:

vector<gameObject*> GameObjects; 

gameObject A* = new gameObject(...init...); 
animatedGameObject B* = new animatedGameObject(...init...); 

GameObjects.push_back(A); 
GameObjects.push_back(B); 

// to access the animatedGameObject functions: 
static_cast<animatedGameObject*>(GameObjects[1])->functionUniqueToAnimated(); 

Опасаясь, как обычно, я обратился к Скотт Мейерс (Эффективное использование C++, 3-е издание), который пишет на эту тему:

Многие программисты считают, что забросы ничего не делать но скажите компиляторам рассматривать один тип как другой, но это ошибочно. Преобразования типов любого типа (либо явные с помощью приводов, либо неявные компиляторами) часто приводят к коду, который выполняется во время выполнения.

Я прочитал его Пункт 27: Минимизацию Кастинг дважды, но учитывая мою неопытность с этим, я борюсь с моей неспособностью ответить на простой вопрос «ЭТО немое, что нужно сделать?»

Следует отметить, что существует несколько причин, почему это является немым, что нужно сделать, что не имеет ничего общего с вызовом static_cast. Вопросы, в порядке важности:

  1. Am Я не вижу некоторые возможные риски, с использованием static_cast в моем примере выше?
  2. Есть ли лучшие структуры данных, чем std::vector для таких подходов? (только если есть очевидное, я не прошу вас провести мое исследование для меня.)

Это мой первый вопрос, задающий здесь вопрос, поэтому приносим извинения заранее, где это необходимо.

+0

+1 для чтения Эффективный C++ – Borgleader

+1

Обычным делом было бы определить 'draw()' как виртуальную функцию в базовом классе. Тогда вы могли бы просто называть его без кастингов, и это было бы правильно. Знаете ли вы об этом, но хотите узнать о эффектах броска конкретно? – jogojapan

+1

'boost :: ptr_vector' – Mehrdad

ответ

7

static_cast не подходит для работы, если вы не знаете, что указатель идет к animatedGameObject, а не к gameObject. Какую структуру данных вы используете для хранения этой информации?

Определение типа производного объекта после базового указателя - это задание динамической отправки или dynamic_cast. В вашем примере вызов GameObjects[1]->draw() должен работать без трансляции, потому что draw должен быть виртуальной функцией. В противном случае вы можете использовать dynamic_cast< animatedGameObject & >(* GameObjects[1]), чтобы утверждать, что объект является анимированнымGameObject, и выбрасывает исключение std::bad_cast, если это не так. (Это будет по-прежнему требует virtual функций в class gameObject, как правило, его деструктора.)

Но делать static_cast к полиморфному производному типу является код запахом.


Также вы спросите std::vector ли хорошая структура данных для этого случая использования. Это, но не вектор «голых» указателей. C++ 11 теперь предоставляет классы управления памятью «умный указатель», которые выполняют для вас new и delete, предоставляя реальным операторам все, кроме устаревших. Посмотрите на std::unique_ptr для этого случая.

+1

Я просто еще один голос/больше видимости для создания 'draw' virtual, чтобы это можно было сделать без кастинга. Это один из * классических * примеров вещей, для которых виртуальные функции работают идеально. –

+1

@jogojapan D'oh, я смутил себя. Пользовательский отладчик может сделать 'unique_ptr' обрабатывать неполиморфные типы полиморфно. Починю. – Potatoswatter

+0

Большое спасибо. Собираюсь идти читать на 'std :: shared_ptr' – ilzxc

1
  1. Если gameObjects [1] не анимированGameObject, приложение (скорее всего) умрет ужасно.
  2. Если draw() - это виртуальный метод, который присутствует в gameObject, преобразование не нужно.

В целом, базовый класс заливки в производный класс небезопасен. В таких ситуациях имеет смысл использовать dynamic_cast. Dynamic_cast возвращает NULL, если преобразование невозможно.

Существуют ли более эффективные структуры данных, чем

Ну, если ваши геймобжекты не удаляются автоматически в другом месте, это может иметь смысл использовать что-то вроде std::vector<std::shared_ptr<gameObject> >. Тем не менее, стандартные общие указатели могут вводить скрытые накладные расходы (дополнительные новые/удаленные, в худшем случае они могут даже вводить многопоточную блокировку мьютекса, ЕСЛИ они предназначены для потокобезопасности), поэтому вы должны убедиться, что эти накладные расходы совместимы с вашими цели.

 Смежные вопросы

  • Нет связанных вопросов^_^