Кто является владельцем вещи в хранилище?
Ваша настоящая проблема - право собственности.В настоящее время ваш Storage
на самом деле не содержит Things
, но вместо этого он оставлен на усмотрение пользователя Storage
для управления временем жизни объектов, которые вы помещаете в него.Это очень сильно противоречит философии стандартных контейнеров.Все стандартные контейнеры C ++ владеют объектами, которые вы в них помещаете, и контейнер управляет их временем жизни (например, вы просто вызываете v.resize(v.size()-2)
для вектора, и последние два элемента уничтожаются).
Почему ссылки?
Вы уже нашли способ заставить контейнер не владеть реальными объектами (используя reference_wrapper
), но нет никаких причин дляСделай так.Класса с именем Storage
я ожидаю, что он будет содержать объекты, а не только ссылки.Более того, это открывает двери для множества неприятных проблем, включая неопределенное поведение.Например, здесь:
void temp(Storage& storage) {
storage.findById(2).add(1);
Thing t4; t4.add(50);
storage.add(t4);
std::cout << storage.findById(4).getValue() << "\n";
}
Вы храните ссылку на t4
в storage
.Дело в том, что t4
время жизни только до конца этой функции, и вы получите висячую ссылку.Вы можете хранить такую ссылку, но это не очень полезно, потому что вам, по сути, не разрешено с ней ничего делать.
Разве ссылки не полезны?
В настоящее время вы можете нажать t1
, изменить его, а затем заметить, что изменения в вещи в Storage
могут быть хорошими, если вы хотите имитировать Java, но в c ++ мы привыкли к контейнерам, делающим копию, когда вы что-то нажимаете(Существуют также методы для создания элементов на месте, если вы беспокоитесь о каких-то бесполезных временных).И да, конечно, если вы действительно хотите, вы можете сделать стандартный контейнер, также содержащий ссылки, но давайте сделаем небольшой обход ...
Кто собирает весь этот мусор?
Может быть, полезно учитывать, что Java является сборщиком мусора, а в C ++ есть деструкторы.В Java вы привыкли к ссылкам, плавающим до тех пор, пока не сработает сборщик мусора. В C ++ вы должны быть супер осведомлены о времени жизни ваших объектов.Это может звучать плохо, но на самом деле оказывается чрезвычайно полезным иметь полный контроль над временем жизни объектов.
Мусор?Что за фигня?
В современном C ++ вы не должны беспокоиться о том, чтобы забыть delete
, а скорее оцените преимущества наличия RAII .Получение ресурсов при инициализации и знание, когда вызывается деструктор, позволяет получить автоматическое управление ресурсами практически для любого типа ресурса, о чем сборщик мусора может только мечтать (думать о файлах, соединениях с базой данных и т. Д.).
«Как я могу исправить код, не удаляя функцию temp () и не управляя памятью вручную?»
Уловка, которая мне очень помогла, заключается в следующем: всякий раз, когда я думаю о себеМне нужно управлять ресурсом вручную, я останавливаюсь и спрашиваю: «Разве кто-то еще не может делать грязные вещи?».Это действительно очень редко, что я не могу найти стандартный контейнер, который делает именно то, что мне нужно из коробки.В вашем случае просто позвольте std::list
сделать «грязную» работу.
Не может быть C ++, если нет шаблона, верно?
Я бы фактически предложил вам сделать Storage
шаблоном, например:
template <typename T>
class Storage {
private:
std::list<T> list;
//....
Тогда
Storage<Thing> thing_storage;
Storage<int> int_storage;
составляют Storage
с, содержащие Thing
с и int
с, соответственно.Таким образом, если вам когда-нибудь захочется экспериментировать со ссылками или указателями, вы все равно можете создать экземпляр Storage<reference_wrapper<int>>
.
Я что-то пропустил? ... может быть, ссылки?
Я не смогу изменить состояние хранимого объекта
Учитывая, что контейнер принадлежит объекту, вы бы предпочли, чтобы пользователь взял ссылку на объект в контейнере,Например, с вектором, который будет
auto t = std::vector<int>(10,0); // 10 element initialized to 0
auto& first_element = t[0]; // reference to first element
first_element = 5; // first_element is an alias for t[0]
std::cout << t[0]; // i dont want to spoil the fun part
Чтобы это работало с вашим Storage
, вам просто нужно заставить findById
вернуть ссылку.В качестве демонстрации:
struct foo {
private:
int data;
public:
int& get_ref() { return data;}
const int& get_ref() const { return data;}
};
auto x = foo();
x.get_ref = 12;
TL; DR
Как избежать ручного управления ресурсами?Пусть кто-нибудь другой сделает это за вас и назовет это автоматическим управлением ресурсами: P