Функция, возвращающая динамически приведенный указатель, возвращает указатель, как если бы он не был приведен вообще - PullRequest
0 голосов
/ 09 ноября 2019

У меня есть базовый класс Component и несколько производных классов, таких как MeshComponent. Класс Entity хранит все Components как std::shared_ptr в std::unordererd_map caaled m_Components, поэтому я создаю следующую функцию для получения компонента с карты:

const std::shared_ptr<Component>& Entity::GetComponent(ComponentType type)
{   
    switch (type)
    {
        case ComponentType::None:
            EG_CORE_ASSERT(false, "Component of type None is not supported!");
            return nullptr;
        case ComponentType::Transform:
            return std::dynamic_pointer_cast<TransformComponent>(m_Components[ComponentType::Transform]);
        case ComponentType::Mesh:
            return std::dynamic_pointer_cast<MeshComponent>(m_Components[ComponentType::Mesh]);
    }
}

ComponentType являетсяКласс enum, содержащий все типы компонентов. Проблема заключается в том, что когда я вызываю эту функцию в своем основном файле, возвращается указатель, я просто указываю на базовый класс и не могу вызвать функцию, специфичную для определенного компонента.

Я пытался привести эти указатели в мои основные файлы. вот так: std::dynamic_pointer_cast<Engine::MeshComponent>(testEntity.GetComponent(Engine::ComponentType::Mesh))->SetVertexArray(m_VertexArray);

Но это возвращает меня к нарушению доступа к памяти.

1 Ответ

0 голосов
/ 09 ноября 2019

Что не так с вашим кодом?

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

Но вы сделали что-то не так: вы возвращаете ссылку, и эта ссылка будет на временный общий указатель, который будет уничтожен, как только вы вернетесь из GetComponent(), вызывая UB.

Вам необходимо переопределить вашу функцию и использовать тип возвращаемого значения, удалив & позади const std::shared_ptr<Component>:

const std::shared_ptr<Component> Entity::GetComponent(ComponentType type)

Это должно решить вашу проблему, если вы сделали downcast дляправильный тип.

что может быть улучшено в вашем коде?

Но вы должны признать, что уныние может пойти не так. Вся цель dynamic_pointer_cast - разрешить такую ​​проверку безопасности. Поэтому вместо:

std::dynamic_pointer_cast<Engine::MeshComponent>(testEntity.GetComponent(Engine::ComponentType::Mesh))->SetVertexArray(m_VertexArray);

сделайте это в два шага:

auto pm = std::dynamic_pointer_cast<Engine::MeshComponent>(testEntity.GetComponent(Engine::ComponentType::Mesh)); 
if (pm) 
    pm->SetVertexArray(m_VertexArray);
else std::cout << "Oh oh ! Something went wrong"<<std::endl; 

Здесь онлайн-демонстрация на минималистском примере. Вы можете поиграть с ним онлайн и поэкспериментировать: добавление & к типу возврата вызовет здесь ошибку времени выполнения.

Полиморфизм не требует приведения.

Если у вас есть код для симуляции и вам приходится выполнять много типов, значит, с дизайном что-то не так.

Во-первых, нет способа создать полиморфное Getcomponent(), которое позволило бы вам немедленно вызвать функцию, которая не существует для полиморфного типа:

  • Здесь вы делаетединамическое приведение в функции, но так как тип возвращаемого значения функции - время компиляции, ваше преобразование в двоичную форму немедленно возвращается в исходное состояние. Вот почему вам нужно (рискованное) снижение с возвращенным указателем.
  • Нет способа сделать это с помощью шаблонов. Потому что шаблоны также основаны на типах времени компиляции.

Что вы можете сделать, это определить три разные функции, каждая из которых возвращает ожидаемый тип. Но тогда вы должны быть особенно осторожны, потому что получение указателя таким образом, не будучи уверенным в том, что указатель найден в первую очередь, и не будучи уверенным, что динамическое приведение завершится успешно, может привести к разыменованию nullptr, который является UBи может привести к сбою вашего программного обеспечения.

Рекомендация:

Постарайтесь сделать функции-члены Component как можно более полиморфными, чтобы пользователю не приходилось знать, является ли он сеткой или нет, когдавызывая функцию. Даункинг должен быть исключением.

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

...