Я работаю с указателями уже несколько лет, но я только недавно решил перейти на интеллектуальные указатели С++ 11 (а именно, уникальный, общий и слабый). Я провел довольно много исследований по ним, и вот те выводы, которые я сделал:
- Уникальные указатели замечательные. Они управляют своей собственной памятью и являются такими же легкими, как и исходные указатели. Предпочитайте unique_ptr над необработанными указателями как можно больше.
- Общие указатели сложны. Они имеют значительные накладные расходы из-за подсчета ссылок. Передайте их по ссылке const или пожалеете об ошибках ваших путей. Они не злые, но их следует использовать экономно.
- Общие указатели должны иметь объекты; используйте слабые указатели, когда владение не требуется. Блокировка weak_ptr имеет эквивалентные служебные данные для конструктора копии shared_ptr.
- Продолжайте игнорировать существование auto_ptr, которое теперь устарело.
Итак, имея в виду эти принципы, я отправился пересматривать свою базу кода, чтобы использовать наши новые блестящие умные указатели, полностью намереваясь очистить доски как можно большего количества исходных указателей. Однако я запутался в том, как лучше использовать интеллектуальные указатели С++ 11.
Предположим, например, что мы разрабатывали простую игру. Мы решаем, что оптимально загружать вымышленный тип данных Texture в класс TextureManager. Эти текстуры сложны, поэтому их невозможно реализовать по значению. Более того, предположим, что игровым объектам нужны конкретные текстуры в зависимости от их типа объекта (например, автомобиль, лодка и т.д.).
До этого я бы загрузил текстуры в вектор (или другой контейнер, например unordered_map), и сохранил указатели на эти текстуры в каждом соответствующем игровом объекте, чтобы они могли ссылаться на них, когда они нуждались в визуализации. Предположим, что текстуры гарантированно переживут свои указатели.
Итак, мой вопрос заключается в том, как наилучшим образом использовать интеллектуальные указатели в этой ситуации. Я вижу несколько вариантов:
-
Храните текстуры непосредственно в контейнере, а затем создайте unique_ptr в каждом игровом объекте.
class TextureManager { public: const Texture& texture(const std::string& key) const { return textures_.at(key); } private: std::unordered_map<std::string, Texture> textures_; }; class GameObject { public: void set_texture(const Texture& texture) { texture_ = std::unique_ptr<Texture>(new Texture(texture)); } private: std::unique_ptr<Texture> texture_; };
Мое понимание этого, однако, состоит в том, что новая текстура будет построена по копиям из переданной ссылки, которая затем будет принадлежать unique_ptr. Это кажется мне очень нежелательным, так как у меня будет столько копий текстуры, сколько игровых объектов, которые ее используют, - побеждая точку указателей (не каламбур).
-
Храните не текстуры напрямую, а их общие указатели в контейнере. Используйте make_shared для инициализации общих указателей. Построить слабые указатели в игровых объектах.
class TextureManager { public: const std::shared_ptr<Texture>& texture(const std::string& key) const { return textures_.at(key); } private: std::unordered_map<std::string, std::shared_ptr<Texture>> textures_; }; class GameObject { public: void set_texture(const std::shared_ptr<Texture>& texture) { texture_ = texture; } private: std::weak_ptr<Texture> texture_; };
В отличие от случая unique_ptr, мне не придется копировать-строить сами текстуры, но рендеринг игровых объектов дорог, так как мне пришлось бы блокировать weak_ptr каждый раз (сложнее, чем копирование нового shared_ptr).
Итак, чтобы понять, мое понимание таково: если бы я использовал уникальные указатели, мне пришлось бы копировать текстуры; альтернативно, если бы я использовал общие и слабые указатели, мне пришлось бы по существу копировать общие указатели каждый раз, когда игровой объект должен быть нарисован.
Я понимаю, что интеллектуальные указатели по своей сути будут более сложными, чем исходные указатели, и поэтому мне придется что-то потерять, но обе эти затраты кажутся выше, чем, возможно, они должны быть.
Может ли кто-нибудь указать мне правильное направление?
Извините за долгое чтение и спасибо за ваше время!