Pull to refresh

Comments 14

По моему опыту, кастомные аллокаторы нужны для двух вещей: обеспечения выравнивания и эффективной аллокации в контейнерах. C++17 очень прокачал обе эти темы с alignas и pmr, так что о перегрузке операторов можно было бы уже забыть, но в статье к сожалению эта тема не раскрыта вообще (а pmr даже не упоминается).

Ещё есть ситуации, где ограничено использование malloc и нужно использовать специальную версию malloc. Например написание расширений для PostgreSQL, там рекомендуется использовать palloc:


When allocating memory, use the PostgreSQL functions palloc and pfree instead of the corresponding C library functions malloc and free. The memory allocated by palloc will be freed automatically at the end of each transaction, preventing memory leaks.
Согласен, что кастомные аллокаторы вещь довольно редкая. Аллокаторы для стандартных контейнеров тема весьма обширная, требует отдельной статьи. Именно там уместно обсудить pmr. На первый взгляд pmr ничего особенного из себя не представляют, достаточно традиционная кастомизация аллокатора с помощью виртуальных функция. Небольшие дополнительные накладные расходы на вызов виртуальных функций и за это полное отделения интерфейса от реализации. Я пока эту тему глубоко не копал, может там действительно есть что-то интересное.
Может быть стоило бы написать о том, чем отличается new int[42]; от new int[42]();/new int[42]{};?

Потому что я видел реальных людей, которые пытылись «лишнее» убрать…
int является тривиальным типом, поэтому при выполнении new int[42] для элементов не будет вызван конструктор по умолчанию (который для int просто обнуляет) и вектор будет инициализирован случайными значениями. Для не тривиальных типов гарантирован вызов конструктора по умолчанию. Два других варианта как раз и гарантируют вызов конструктора по умолчанию для любых типов, в том числе и тривиальных и в приведенных примерах вектор будет обнулен. Вариант с фигурными скобками позволяет задать произвольные инициализирующие значения для элементов вектора.
Спасибо — я-то про это знаю… но ваша статья вроде как для новичков… они могут не знать. Ну раз уж всё равно упоминаете про инициализаторы — то упомяните ещё и том, что память может быть неинициализирована.

Для выходцев со всяких Java/PHP/Python это часто оказывается неожиданностью… а людей, переходящих с C на C++ сегодня немного…
Я бы включил эти вопросы в статью про тривиальные типы и неинициализированные переменные. Тема на самом деле довольно интересная, правда материала не очень много, так на пару страниц.

Не знал про пользовательские new/delete, даже стало интересно применяет ли их кто.


Мне нравится такой формат — много деталей в одном месте и понятным образом описано.

Не знал про пользовательские new/delete, даже стало интересно применяет ли их кто.
Крайне редко. У них есть недостаток: вы можете сделать, условно, operator new для какого-то класса Node… но вот для какого-нибудь std::set<Node> они применяться не будут. И сделать тут ничего нельзя.

Потому обычно используются аллокаторы… а перегружаемые new/delete так и остались памятником плохому дизайну…

Пришло в голову что-то вроде выделение в определенной арене:auto t = new(current_frame_arena) Animation(...), но если подумать, это не есть что-то такое что нельзя было бы сделать без этой фичи (auto t = current_frame_arena.Allocate<Animation>(...)).


Вообще у перегрузки new/delete я в итоге видел смысл только в какой-то инструментации (поиск утечек и некоторых других ошибок). Placement new фактически на уровне языка решает множество сценариев когда есть желание оптимизировать выделение и для этого перегружать обычно ничего не надо.

Вы смотрите с позиций сегодняшнего дня. Когда есть placement new, вложенные шаблоны и прочее.

Но это всё появилось позже!
Спасибо! Рад, что формат понравился. Надеюсь через пару недель будет еще статья. Действительно пользовательские new/delete используются редко (лично я сам никогда). Но мне хотелось закрыть тему про перегрузку в C++ и без пользовательские new/delete это бы не получилось.

Здравствуйте,
Вызов ::operator new(size) — это вызов стандартной функции выделения памяти, которая в качестве параметра принимает требуемый размер. Эта фунция только выделяет память, ничего больше.
Вызов ::new(size) не будет компилироваться, так как в этом случае синтаксически правильной формой должна быть ::new X(/* аргументы конструктора */), где X — это имя класса. В данном случае ::new — это оператор new, который не только выделяет память, но и вызывает конструктор, то есть инициализирует объект.
С уважением. Дмитрий Пономарев.

Sign up to leave a comment.

Articles