КД>В моем случае счетчик хранится в блоке с самими данными.
КД>Я этот shared_ptr поэтому и не люблю. Даже кушать не могу. То есть нигде не использую.
Если создаёшь shared_ptr через make_shared, то счётчик ссылок тоже вместе с данными будет. Я бы для начала в свой велосипед как раз shared_ptr и засунул. Посмотрел бы, что да как, а потом, если надо, то дальше бы оптимизировал.
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>Атомарность присваивания ссылок в Яве позволяет очень здорово работать с иммутабельными объектами (теми же строками). SVZ>На многопоточных задачах можно не заморачиваться синхронизацией — если потребовалось модифицировать объекто, то тупо заменяем старый объект новым. GC потом приберет освободившихся.
SVZ>Чтобы такое замутить на плюсах приходится городить шаред поинтер, подсчет ссылок.
SVZ>А "грохать разом на выходе" я предложил, но Дмитрий не хочет
У меня "на выходе" грохается словарь.
Но строки продолжают жить, пока не завершаться задачи в которых они используются.
Эти задачи, сами по себе, тоже дополнительную память жрут. И кроме них там тоже много кто жрет памяти. Поэтому постепенно освобождение памяти более ненужных строк весьма кстати.
Собственно говоря, ответ мне тут уже подсказали — make_shared.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, andyp, Вы писали:
КД>>В моем случае счетчик хранится в блоке с самими данными.
A>Если создаёшь shared_ptr через make_shared, то счётчик ссылок тоже вместе с данными будет.
Здравствуйте, andyp, Вы писали:
ETI>>С данными (байтами) самой строки?
A>Нет, конечно.
Тогда это два выделения вместо трех, как я понял ТС хотел хранить счетчик с данными строки и получить одну аллокацию из кучи на формирование объекта строки.
ETI>Тогда это два выделения вместо трех, как я понял ТС хотел хранить счетчик с данными строки и получить одну аллокацию из кучи на формирование объекта строки.
Я отчасти поэтому и писал про "что да как". Если с++17, то можно shared_ptr<unsigned char[]> использовать, но тогда много чего придется делать заново руками.
Здравствуйте, EreTIk, Вы писали: ETI>Здравствуйте, andyp, Вы писали: ETI>>>С данными (байтами) самой строки? A>>Нет, конечно. ETI>Тогда это два выделения вместо трех, как я понял ТС хотел хранить счетчик с данными строки и получить одну аллокацию из кучи на формирование объекта строки.
Да, точно. Я не сообразил, что std::wstring, хотя и хранится в блоке со счетчиком ссылок, сами данные будет хранить в отдельном блоке памяти
То есть, все таки с shared_ptr будет два блока вместо одного.
---
Я ради эксперимента перевел на std::shared_ptr<const std::wstring>.
Пришлось исправлять сравнения (нужно разыменовывать shared_ptr) и написать замену для std::less<std::shared_ptr<const std::wstring> — опять же ради разыменования.
Здравствуйте, Коваленко Дмитрий, Вы писали: КД>Собственно говоря, ответ мне тут уже подсказали — make_shared.
std::make_shared<const std::string> будет оптимально (одним блоком) только для коротких строк, которые помещаются в SSO-буфер std::string'а.
Для длинных строк будет еще аллокация для буфера строки.
Если из интерфейса std::string кроме compare и c_str ничего не нужно, то можно сделать строчку оптимальнее.
К примеру, нет необходимости хранить size и capacity и счетчик всегда будет в одном блоке с символами строки.
Я решал похожую задачу с константными строками и не стал связываться с подсчетом ссылок.
Сделал пул памяти поверх кучи Windows (HeapCreate, HeapAlloc, HeapDestroy) с выделением от 64K и больше.
Нуль-терминированные строки "ложились" в блоках одна за другой.
При создании строки наружу выдавался const char*, удаление отдельной строки не предусматривалось.
Когда большой объект (вроде DOM — в моем случае), владеющий всеми этими строками, становился не нужен, то просто удалялся весь пул (HeapDestroy).
Все получается предельно компактно, шустро работает и потокобезопасно в пределах времени жизни большого объекта.
Реализация занимает 100 строчек, включая ассерты.
Здравствуйте, EreTIk, Вы писали:
КД>>>В моем случае счетчик хранится в блоке с самими данными.
A>>Если создаёшь shared_ptr через make_shared, то счётчик ссылок тоже вместе с данными будет.
ETI>С данными (байтами) самой строки?
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>В STL что-то подобное для константных строк есть?
Использую std::shared_ptr<const std::[w]string> + небольшой лисапед со словарём для абсолютно тех же целей.
Делалось это ещё до std::string_view. Если бы делал сейчас, то наружу торчал бы только std::string_view
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>string_view — это все таки штука для доступа/представления, а не для хранения. Насколько я понимаю.
Если твои данные гарантированно не меняются, наружу можно выдавать std::string_view. А как оно внутри хранится (в данном случае это std::shared_ptr<const std::string>) это уже не важно.
Я бы сейчас вот так сделал (псевдокод):
std::string_view ConstStrDict::get(std::string_view str)
{
auto it = dict_.find(str);
if (it == dict_.end())
it = dict_.insert(std::pair { str, std::make_shared<std::string>(str) });
return *it->second;
}
Сейчас же выдаётся наружу std::shared_ptr<const std::string>. Просто лень переписывать
Здравствуйте, ArtDenis, Вы писали:
КД>>string_view — это все таки штука для доступа/представления, а не для хранения. Насколько я понимаю.
AD>Если твои гарантированно данные не меняются, наружу можно выдавать std::string_view. А как оно внутри хранится (в данном случае это std::shared_ptr<const std::string>) это уже не важно.
, что словарь хранится только в процессе формирования задач. КД>Потом словарь грохается, но строки продолжают жить своей жизнью и постепенно изничтожаются. КД>Если в задачах удерживать словарь, то он будет удерживать все строки до завершения последней задачи. А мне такое не надо.
Да, всё зависит от задачи. Универсального решения нету.
Кстати, можно периодически уничтожать в словаре элементы, на которые нету ссылок. Тогда не будут накапливаться уже не используемые строки. Но тогда наружу надо выдавать std::shared_ptr<std::string>