это был nullptr при добавлении unique_ptr в unordered_map - PullRequest
0 голосов
/ 08 января 2020

Я создаю простой демонстрационный проект, но я застрял при добавлении unique_ptr в неупорядоченный список, каким-то образом он добавляет ключ, но не указатель, и позже, когда я пытаюсь вызвать метод по указателю, я получаю 'this was nullptr error'.

У меня есть базовый класс Scene, который отвечает за хранение указателей и добавление их на карту следующим образом:

void Scene::addGameObject(const std::string& name, const GameObjectParams& params)
{
    m_objects.try_emplace(name, std::make_unique<GameObject>(params));
}

bool Scene::containsGameObject(const std::string& name)
{
    if (m_objects[name] != NULL)
        return true;

    return false;
}

GameObject& Scene::getGameObject(const std::string& name)
{
    auto it = m_objects.find(name);

    if (it != m_objects.end())
        return *it->second;


}

void Scene::drawScene()
{
    for (const auto& object : m_objects)
        object.second->draw(); // <--------here the error gets thrown
}

Если я позвоню этот метод из класса, который имеет scene в качестве члена, работает нормально, например:

//GameLayer.h
class GameLayer 
{
private:
    // GameScene is derived class from Scene
    GameScene m_gameScene;
};

//GameLayer.cpp
m_gameScene.addGameObject(
   "player",
   {
       Vector3f(0.0f, 0.0f, 1.0f),
       Vector3f(100.0f, 70.0f, 1.0f),
       Quaternion(),
       Vector4f(1.0f),
       fighter
    }
);

Но теперь, когда я пытаюсь добавить новый объект из самого производного класса, он добавляет ключ, но нет указатель

void GameScene::spawnLaser(Vector3f pos)
{
    for (int i = 0; i < 100; i++)
    {
        std::string name = std::to_string(i);
        if (!this->containsGameObject(name))
        {
            m_lasers.emplace_back(name);
            this->addGameObject(
                "laser",
                GameObjectParams {
                    Vector3f(0.0f, 0.0f, 1.0f),
                    Vector3f(100.0f, 70.0f, 1.0f),
                    Quaternion(),
                    Vector4f(1.0f),
                    nullptr
                }
            );
            return;
        }
    }
}

GameObject - это объект, который должен быть создан с помощью std::make_unique

GameObject::GameObject(const GameObjectParams& params)
    : m_position(params.position.x, params.position.y, params.position.z),
    m_scale(params.scale.x, params.scale.y, params.scale.z),
    m_rotation(),
    m_texture(params.texture),
    m_color(params.color)
{
}

void GameObject::setTexture(cheetah::Texture* texture)
{
    m_texture = texture;
}

void GameObject::setPosition(const cheetah::Vector3f& position)
{
    m_position.x = position.x;
    m_position.y = position.y;
    m_position.z = position.z;
}

void GameObject::translate(const cheetah::Vector3f& position)
{
    m_position.x += position.x;
    m_position.y += position.y;
    m_position.z += position.z;
}


void GameObject::draw()
{
    // 'this was nullptr' gets thrown here
    if (m_texture != nullptr)
    {
        Renderer2D::drawQuad(DrawTexturedQuadParams{ m_position, m_scale, m_rotation, Vector4f(1.0f), m_texture });
    }
    else
    {
        Renderer2D::drawQuad(DrawQuadParams{ m_position, m_scale, m_rotation, m_color });
    }
}

Кажется, что make_unique не создает новый объект, но я понятия не имею почему.

1 Ответ

4 голосов
/ 08 января 2020

В этом коде:

bool Scene::containsGameObject(const std::string& name)
{
    if (m_objects[name] != NULL)
        return true;

    return false;
}

Если name не существует на карте, operator[] карты добавит name с по умолчанию std::unique_ptr который содержит nullptr. Вам нужно использовать m_objects.find(name) вместо m_objects[name]:

bool Scene::containsGameObject(const std::string& name) const
{
    return (m_objects.find(name) != m_objects.end());
}

В этом коде:

GameObject& Scene::getGameObject(const std::string& name)
{
    auto it = m_objects.find(name);

    if (it != m_objects.end())
        return *it->second;


}

Если name не найден на карте, возвращаемое значение неопределенный . Вам нужно либо:

  • вернуть указатель вместо ссылки, чтобы вы могли вернуть nullptr, если name не найден (а затем обновить вызывающую программу для проверки на nullptr):
GameObject* Scene::getGameObject(const std::string& name)
{
    auto it = m_objects.find(name);

    if (it != m_objects.end())
        return it->second.get();

    return nullptr;
}
  • продолжать возвращать ссылку, но выдает исключение, если name не найдено:
GameObject& Scene::getGameObject(const std::string& name)
{
    // std::unordered_map::at() throws std::out_of_range if the key is not found...
    return *(m_objects.at(name)->second);
}
...