Как превратить вектор необработанных указателей в вектор уникальных указателей?

#include <vector>

enum ListOfGameStates
{
    // List of game states
};

class GameState()
{
    public:
        GameStates(); // Initializes protected (global) variables
        virtual ListOfGameStates run() = 0;
    protected:
        // Heavyweigh resource managers containing all resources and other global vars
}

class GameStateManager()
{
    public:
        GameStateManager();  // Creates all game states
        ~GameStateManager(); // Deletes all game states
        void run();          // Switches from one state to another state
    private:
        // A vector of raw pointers to game states. GameState is a base class.
        std::vector<GameState*> game_states_container;
}

Я хочу избавиться от необработанных указателей, чтобы не беспокоиться об исключениях и очистке. Есть ли простое решение (я действительно тупой подросток) или оно того не стоит? Спасибо!


person Community    schedule 01.02.2015    source источник
comment
Извините, я был сбит с толку, потому что 'push_back' не работал. Теперь все в порядке   -  person    schedule 01.02.2015
comment
Как правило, вероятно, неплохо включить в ваш вопрос то, что не сработает.   -  person Chris Drew    schedule 01.02.2015
comment
Похоже, вашему классу GameState нужен деструктор virtual.   -  person Galik    schedule 01.02.2015
comment
@Галик Почему? В игровых состояниях нет указателей, все большие объекты глобальны. Другие — самоуничтожающиеся умные классы.   -  person    schedule 01.02.2015
comment
GameState имеет чисто виртуальную функцию, что означает, что вы будете использовать только подклассы, и эти подклассы будут удалены (умным указателем) из базового указателя. Без виртуального dtor это поведение undefined.   -  person Galik    schedule 01.02.2015
comment
Хорошие книги: stackoverflow.com/ вопросы/388242/   -  person Galik    schedule 01.02.2015


Ответы (1)


Просто измените свой вектор на:

std::vector<std::unique_ptr<GameState>> game_states_container;

И избавьтесь от любых delete в вашем деструкторе. На самом деле вы, вероятно, можете полностью избавиться от деструктора, если у него нет других задач.

unique_ptr нельзя копировать, но можно перемещать, поэтому стоит иметь некоторое представление о семантике перемещения C++11. Если вы хотите добавить unique_ptr в свой контейнер, вы можете использовать push_back при условии, что вы передаете временное значение, например, возвращаемое значение функции:

game_states_container.push_back(createGameState());
game_states_container.push_back(std::make_unique<GameStateA>());  // C++14

Или, если у вас есть локальная переменная unique_ptr, вы можете использовать std::move, чтобы переместить ее в вектор:

std::unique_ptr<GameState> game_state = std::make_unique<GameStateA>();  // C++14
// auto game_state = std::unique_ptr<GameState>(new GameStateA);  // C++11
...
game_states_container.push_back(std::move(game_state));

Рекомендуется помещать необработанные указатели в unique_ptr, как только вы их new (или предпочтительно использовать std::make_unique). В противном случае, если между выделением и переносом в unique_ptr возникнет исключение, у вас будет утечка памяти.

Это не связано с unique_ptr, но ваш класс GameState должен иметь виртуальный деструктор.

Демо

person Chris Drew    schedule 01.02.2015
comment
Попробуйте и посмотрите. Также прочтите свою книгу по C++11 до конца, прежде чем продолжить. - person Lightness Races in Orbit; 01.02.2015
comment
Думаю, мне нужна еще одна книга. - person ; 01.02.2015
comment
На самом деле, Крис, ваш ответ сейчас немного вводит в заблуждение: push_back будет отлично работать на unique_ptr, даже без std::move — пока отправляемый объект является временным. emplace_back не нужен, потому что unique_ptr не копируется, он необходим, потому что нет неявного преобразования необработанного указателя в std::unique_ptr. - person Konrad Rudolph; 01.02.2015
comment
@KonradRudolph Надеюсь, теперь это не вводит в заблуждение. - person Chris Drew; 01.02.2015
comment
@Chris Теперь все идеально. - person Konrad Rudolph; 01.02.2015
comment
Не знаю, стоит ли об этом упоминать, но emplace_back перенаправляет свои аргументы фундаментальному конструктору T, когда выполняется размещение. Таким образом, эквивалентным другим методам вставки является не emplace_back(), а emplace_back(new GameStateDerivative);. Конструктор для std::unique_ptr<> получит выделенный объект и станет владельцем, избегая необходимости последующего reset(new GameStateDerivative) (что вы все равно можете сделать, конечно). Кроме того, make_unique — это функция C++14 для тех, кто все еще находится в окопах C++11 (то есть для большинства из нас). - person WhozCraig; 01.02.2015
comment
@WhozCraig Теперь у нас есть make_unique (который доступен в Visual Studio 2013). Я полностью избегаю использования new и предпочитаю push_back(std::make_unique<GameStateDerivative>()). - person Chris Drew; 01.02.2015
comment
Да, теперь у нас есть make_unique — к сожалению, у нас его пока нет. Боже, я все еще застрял в полусырой производной MS 03x, созданной с VS2010 на работе (да, тьфу). Компания укрепления FTL. * - person WhozCraig; 01.02.2015
comment
@WhozCraig, но что делает emplace_back(new GameStateDerivative), если вектор пытается перераспределить и выдает bad_alloc? Конструктор для unique_ptr становится владельцем только в том случае, если вектор заходит достаточно далеко, чтобы использовать этот конструктор. См. stackoverflow.com/a/19414387/981959. - person Jonathan Wakely; 01.02.2015
comment
@JonathanWakely Я думаю, что полностью избавлюсь от предложения emplace_back, я не могу придумать вескую причину для его использования в этом контексте. - person Chris Drew; 01.02.2015
comment
@ChrisDrew, согласен. emplace_back полезен, чтобы избежать временного, но его не следует использовать только для выполнения неявного преобразования, которое в противном случае не удалось бы скомпилировать. - person Jonathan Wakely; 01.02.2015