Память
Стек и куча, RAII, выравнивание, умные указатели и placement new.
34 вопросов
JuniorТеорияОчень частоВ чём разница между delete и delete[]? Что будет при несоответствии?
В чём разница между delete и delete[]? Что будет при несоответствии?
delete освобождает один объект, вызывая его деструктор один раз; delete[] освобождает массив new[], вызывая деструктор для каждого элемента. Несоответствие — UB и может испортить метаданные кучи, пропустить деструкторы или привести к падению.
Типичные ошибки
- ✗Использовать
deleteвместоdelete[]для динамически выделенного массива — классическое UB, особенно для не-тривиальных типов - ✗Не знать, что
delete nullptrвсегда безопасен и не делает ничего — проверка перед удалением указателя не нужна - ✗Думать, что несоответствие
delete[]для встроенных типов (int, char) безвредно — всё равно UB, так как метаданные аллокатора отличаются
Уточняющие вопросы
- →Как умные указатели (
unique_ptr<T[]>) корректно обрабатывают форму массива? - →Что делает компилятор при
new int[0]?
JuniorТеорияОчень частоЧто такое утечка памяти? Что произойдёт, если забыть вызвать delete?
Что такое утечка памяти? Что произойдёт, если забыть вызвать delete?
Утечка памяти — это динамически выделенная память, которая никогда не освобождается; если delete пропущен, деструктор не вызывается, блок кучи остаётся занятым и со временем приводит к OOM в долгоживущих процессах.
Типичные ошибки
- ✗Вызывать
deleteдля одного и того же указателя дважды (double-free) — неопределённое поведение, потенциальная порча кучи - ✗Использовать
deleteвместоdelete[]для массивов или наоборот — неопределённое поведение - ✗Рассчитывать, что ОС освободит утечки — допустимо только для короткоживущих утилит, никогда для серверов или игр
Уточняющие вопросы
- →Как обнаружить утечки памяти в C++? Назовите хотя бы два инструмента.
- →Как RAII предотвращает утечки памяти по сравнению с ручным
delete?
JuniorТеорияОчень частоВ чём разница между new/delete и malloc/free?
В чём разница между new/delete и malloc/free?
new выделяет память и конструирует объект; delete вызывает деструктор и освобождает память. malloc/free работают только с сырыми байтами и ничего не знают о конструкторах, деструкторах и типах C++.
Типичные ошибки
- ✗Смешивать семейства выделения: malloc с delete или new с free
- ✗Использовать delete вместо delete[] для массивов
- ✗Забывать, что placement new требует явного вызова деструктора, а не delete
Уточняющие вопросы
- →Почему delete[] отличается от delete?
- →Когда нужен placement new?
JuniorТеорияОчень частоЧто такое указатель? Размер, операции и арифметика указателей.
Что такое указатель? Размер, операции и арифметика указателей.
Указатель хранит адрес объекта; его размер зависит от платформы (8 байт на 64-битной) независимо от типа. Операции: *p, &x, p->m. Арифметика определена только внутри массивов: p + n сдвигает на n * sizeof(*p) байт.
Типичные ошибки
- ✗Делать арифметику указателей на не-массивном указателе — технически UB, даже если 'работает'
- ✗Сравнивать
sizeof(int*)сsizeof(int)и считать их равными — неверно на 64-битной архитектуре - ✗Использовать
intдля хранения адреса указателя — используйтеuintptr_tилиintptr_tиз<cstdint>
Уточняющие вопросы
- →В чём разница между нулевым указателем и висячим указателем?
- →Почему
void*особенный и когда вы его использовали бы?
JuniorТеорияОчень частоЧто такое RAII и почему он лежит в основе управления ресурсами в C++?
Что такое RAII и почему он лежит в основе управления ресурсами в C++?
Resource Acquisition Is Initialization: ресурс захватывается в конструкторе и освобождается в деструкторе. C++ гарантирует вызов деструктора даже при раскрутке стека, поэтому RAII делает очистку автоматической и exception-safe.
Типичные ошибки
- ✗Написание конструктора копирования, который копирует сырой дескриптор ресурса, что приводит к двойному освобождению
- ✗Хранение RAII-объектов в сырых указателях с ручным удалением — это обходит RAII
- ✗Использование RAII-объектов в C-массивах без правильной семантики копирования/перемещения
Уточняющие вопросы
- →Напишите минимальную RAII-обёртку для файлового дескриптора POSIX.
- →Как unique_ptr реализует RAII? Каковы накладные расходы?
JuniorТеорияОчень частоВ чём разница между выделением памяти на стеке и в куче?
В чём разница между выделением памяти на стеке и в куче?
Стек — автоматический, LIFO, ограничен (обычно 1–8 МБ); освобождение при выходе из области видимости. Куча — ручное или через умные указатели, практически без ограничений, но с накладными расходами аллокатора и фрагментацией.
Типичные ошибки
- ✗Возврат указателя или ссылки на локальную переменную на стеке — после выхода из функции она становится висячей
- ✗Хранение больших объектов на стеке и переполнение стека
- ✗Ручное управление памятью в куче с помощью raw new/delete вместо RAII-обёрток
Уточняющие вопросы
- →Что такое переполнение стека? Как его обнаружить?
- →Когда накладные расходы на выделение в куче оправданы?
MiddleТеорияОчень частоЧем отличаются unique_ptr, shared_ptr и weak_ptr?
Чем отличаются unique_ptr, shared_ptr и weak_ptr?
unique_ptr владеет объектом единолично и только перемещается. shared_ptr разделяет владение через control block со счётчиками ссылок. weak_ptr наблюдает за тем же control block, но не продлевает жизнь объекта; он нужен для разрыва циклов и проверки, жив ли объект.
Типичные ошибки
- ✗Использовать shared_ptr по умолчанию вместо явного моделирования владения
- ✗Создавать два shared_ptr из одного raw pointer и получать два control block
- ✗Использовать weak_ptr без lock() перед доступом к объекту
Уточняющие вопросы
- →Что хранится в control block у shared_ptr?
- →Почему циклы из shared_ptr могут приводить к утечкам?
JuniorТеорияЧастоВ чём разница между calloc и malloc?
В чём разница между calloc и malloc?
malloc(size) выделяет неинициализированные байты; calloc(count, size) выделяет count * size байт, обнуляет их и проверяет умножение на переполнение. Оба освобождаются free, никогда delete.
Типичные ошибки
- ✗Использовать
callocи считать, что объект инициализирован значением — нулевые байты не равны корректному объекту C++ для не-POD типов - ✗Освобождать память, выделенную через
calloc, с помощьюdelete— UB; нужно использоватьfree - ✗Не проверять возвращаемое значение на null — обе функции возвращают null при ошибке выделения
Уточняющие вопросы
- →Что такое
aligned_alloc(C++17) и когда он нужен? - →Чем
newотличается отmallocпомимо вызова конструкторов?
JuniorТеорияЧастоКак работает std::unique_ptr?
Как работает std::unique_ptr?
RAII-обёртка с нулевыми накладными расходами, эксклюзивно владеющая heap-объектом — некопируемая, но перемещаемая. При уничтожении или reset вызывает delete (или custom deleter). Владение передаётся явно через std::move(up).
Типичные ошибки
- ✗Пытаться скопировать
unique_ptr— он некопируемый по дизайну; используйтеstd::moveдля передачи - ✗Конструировать
unique_ptrиз сырого указателя, полученного в другом месте — создаёт путаницу владения; используйтеmake_uniqueили явную передачу владения - ✗Хранить
unique_ptrв контейнере, а затем копировать контейнер — контейнер тоже становится некопируемым
Уточняющие вопросы
- →Что такое пользовательский deleter для
unique_ptrи когда он нужен? - →Чем
unique_ptr<T[]>отличается отunique_ptr<T>в части операции удаления?
MiddleПроизводительностьЧастоЧто такое выравнивание памяти и как оно влияет на расположение полей структуры и производительность?
Что такое выравнивание памяти и как оно влияет на расположение полей структуры и производительность?
Выравнивание требует, чтобы адрес типа был кратен его alignof (например, int на 4-байтовой границе). Компилятор вставляет padding, поэтому размер структуры ≠ сумме полей. Невыровненный доступ вызывает аппаратные сбои на строгих CPU и лишние шинные транзакции на остальных.
Типичные ошибки
- ✗Допущение, что sizeof(struct) равен сумме размеров полей
- ✗Использование reinterpret_cast между указателями с разными требованиями к выравниванию
- ✗Применение __attribute__((packed)) без понимания стоимости невыровненных операций чтения
Уточняющие вопросы
- →Как работают alignas и alignof? Приведите пример.
- →Когда нужно сверхвыровненное хранилище (например, alignas(64) для строк кэша)?
MiddleДебаггингЧастоЧто произойдёт, если записать данных больше, чем было выделено (переполнение буфера)?
Что произойдёт, если записать данных больше, чем было выделено (переполнение буфера)?
Запись за пределы выделенного региона портит соседнюю память: на стеке перезаписывается адрес возврата (stack smashing); на куче — метаданные аллокатора. Обнаруживается через AddressSanitizer, Valgrind или стековые канарейки.
Открыть задачу →Типичные ошибки
- ✗Использовать C-стиль
strcpy/getsбез проверки размера — эти функции являются наиболее частым источником переполнений стекового буфера - ✗Выделять
nбайт, но записыватьn+1(нулевой терминатор) — классическая ошибка на единицу при работе со строками - ✗Думать, что
std::stringиstd::vectorпредотвращают все переполнения — некорректные границы при ручном индексировании (operator[]) всё равно вызывают переполнение
Уточняющие вопросы
- →Что такое ASLR и как оно усложняет (но не делает невозможными) эксплойты переполнения буфера?
- →Как стековые канарейки обнаруживают переполнения стекового буфера?
MiddleДебаггингЧастоЧто произойдёт, если вызвать free() дважды для одного и того же указателя?
Что произойдёт, если вызвать free() дважды для одного и того же указателя?
Двойное освобождение — неопределённое поведение: портит метаданные кучи, хранящиеся рядом с выделениями, и может вызвать падение, молча испортить данные или создать уязвимость. AddressSanitizer (-fsanitize=address) обнаруживает это мгновенно во время выполнения.
Типичные ошибки
- ✗Вызывать
deleteдля указателя, который уже был удалён — то же UB, что и double-free - ✗Не обнулять указатель после free, затем проверять
if (ptr)перед повторным освобождением — устаревшее ненулевое значение проходит проверку - ✗Разделять сырой указатель между двумя контейнерами, оба из которых пытаются освободить его при уничтожении
Уточняющие вопросы
- →Что такое уязвимость use-after-free и чем она отличается от double-free?
- →Как AddressSanitizer обнаруживает double-free без аппаратной поддержки?
MiddleДебаггингЧастоПочему это утекает, когда doWork бросает исключение?
Почему это утекает, когда doWork бросает исключение?
Если doWork бросает исключение, delete[] buf пропускается при раскрутке стека, и буфер утекает — ручные new/delete не безопасны при исключениях. Решение через RAII: std::vector<int> buf(1000); или auto buf = std::make_unique<int[]>(1000);, освобождаемые автоматически при раскрутке.
Типичные ошибки
- ✗Считать, что ОС возвращает утёкшую кучу при исключении внутри работающего процесса
- ✗Думать, что ручные new/delete безопасны при исключениях
- ✗Перехватывать и глотать исключение вместо использования RAII
Уточняющие вопросы
- →Какую гарантию безопасности при исключениях даёт функции вариант с RAII?
- →Почему раскрутка стека вызывает деструкторы, но не выполняет пропущенные
delete?
MiddleДебаггингЧастоЧто не так с delete arr после new int[100]?
Что не так с delete arr после new int[100]?
Неопределённое поведение: память из new[] нужно освобождать через delete[], а не delete. Их несоответствие — UB, обычно утечка или повреждение кучи. Решение: написать delete[] arr или отказаться от сырых массивов в пользу std::vector<int> или std::make_unique<int[]>(100).
Типичные ошибки
- ✗Считать
deleteиdelete[]взаимозаменяемыми - ✗Думать, что несоответствие важно только для типов с деструкторами
- ✗Ожидать, что компилятор поймает это несоответствие
Уточняющие вопросы
- →Почему
delete[]нужно знать число элементов, аdelete— нет? - →Как
std::make_unique<int[]>(n)полностью устраняет этот риск?
MiddleДебаггингЧастоЧто произойдёт при обращении к памяти после delete? Всегда ли будет crash?
Что произойдёт при обращении к памяти после delete? Всегда ли будет crash?
Доступ после delete — неопределённое поведение. Программа может упасть, выглядеть рабочей, прочитать старые данные, испортить другой объект или сломаться позже. Аллокатор может сразу переиспользовать этот блок, а стандарт C++ не даёт гарантий.
Открыть задачу →Типичные ошибки
- ✗Считать отсутствие crash доказательством корректности кода
- ✗Обнулять один указатель и забывать, что другие алиасы остались висячими
- ✗Возвращать ссылки или указатели на локальные переменные
Уточняющие вопросы
- →Как AddressSanitizer и Valgrind находят use-after-free?
- →Как RAII уменьшает число таких ошибок?
MiddleТеорияЧастоКак std::weak_ptr ломает циклы владения и как безопасно использовать lock()?
Как std::weak_ptr ломает циклы владения и как безопасно использовать lock()?
Циклы, где A и B держат shared_ptr друг на друга, никогда не дойдут до refcount 0 — утечка. Замените одно направление на weak_ptr (наблюдает, не владея); доступ через wp.lock(), атомарно возвращающий shared_ptr или null.
Типичные ошибки
- ✗Вызывать
expired()и считать объект живым — гонка - ✗Забывать, что
lock()для истёкшего возвращает null — проверка обязательна - ✗Применять
weak_ptrдля parent→child владения, где хватаетunique_ptr
Уточняющие вопросы
- →Чем
weak_ptr::lockотличается отweak_ptr::expired? - →Как
enable_shared_from_thisсоздаёт начальный weak_ptr?
JuniorТеорияИногдаЧто такое realloc и когда он используется?
Что такое realloc и когда он используется?
realloc(ptr, newSize) меняет размер блока malloc/calloc на месте, если возможно, иначе выделяет новый блок, побайтово копирует и освобождает старый. При неудаче возвращает null, не освобождая исходный. В C++ безопасен только для тривиально перемещаемых типов — ctor/dtor не вызываются.
Типичные ошибки
- ✗Писать
ptr = realloc(ptr, n)— если realloc вернёт null, исходный указатель теряется и память утекает - ✗Использовать realloc для указателя, полученного от
new— неопределённое поведение - ✗Полагаться на realloc для не-POD объектов — их конструкторы перемещения и деструкторы не вызываются
Уточняющие вопросы
- →Как
std::vectorреализует рост безrealloc? - →Что такое
std::is_trivially_relocatable(предложено) и почему это важно для аллокаторов?
MiddleТеорияИногдаЧто такое std::allocator и зачем писать собственный аллокатор?
Что такое std::allocator и зачем писать собственный аллокатор?
std::allocator<T> — это аллокатор по умолчанию, через который контейнеры получают и освобождают сырую память: allocate/deallocate ворочают байты, а конструирование — отдельный шаг. Собственный аллокатор пишут ради пулов, контроля выравнивания, размещения данных в аренах или shared memory либо инструментирования выделений.
Типичные ошибки
- ✗Путать
allocateс конструированием объекта —allocateлишь возвращает сырую память - ✗Писать аллокатор с состоянием, игнорируя трейты
propagate_on_container_* - ✗Считать, что свой аллокатор меняет размер или раскладку
T, а не его хранилище
Уточняющие вопросы
- →Как
std::allocator_traitsотвязывает контейнеры от деталей аллокатора? - →Какие проблемы возникают с stateful-аллокатором при копировании и swap контейнера?
MiddleТеорияИногдаКак обнаружить утечки памяти в C++?
Как обнаружить утечки памяти в C++?
Используйте Valgrind (--leak-check=full, без перекомпиляции), AddressSanitizer (-fsanitize=address, ~2x замедление, хорош для CI) или LeakSanitizer (-fsanitize=leak, только утечки и быстрее). На Windows: CRT debug heap или Dr. Memory.
Типичные ошибки
- ✗Запускать проверку утечек только вручную — должна быть автоматизирована в CI, чтобы регрессии обнаруживались немедленно
- ✗Игнорировать утечки 'still reachable' в Valgrind — указатели в глобальных или статических переменных всё ещё доступны, но никогда не освобождаются; для долгоживущих сервисов это реальные утечки
- ✗Не тестировать с реальной нагрузкой — утечки в редко выполняемых путях видны только при реалистичном использовании
Уточняющие вопросы
- →Как AddressSanitizer отслеживает выделения кучи для обнаружения use-after-free и выхода за границы?
- →Что такое профилирование кучи и чем оно отличается от обнаружения утечек?
MiddleТеорияИногдаЗанимает ли ссылка память и как она реализована на уровне ABI?
Занимает ли ссылка память и как она реализована на уровне ABI?
Логически ссылка — псевдоним; стандарт не диктует представление. На практике ссылки реализуются указателями — поле-ссылка добавляет размер указателя, ссылочный параметр передаётся как указатель. Компилятор может полностью их убрать.
Типичные ошибки
- ✗Пытаться взять адрес ссылки и ждать значение, отличное от адреса цели
- ✗Считать, что ссылочные поля не увеличивают размер класса — обычно добавляют размер указателя
- ✗Путать «ссылка — псевдоним» и «нет представления» — абстрактно верно, конкретно это указатель
Уточняющие вопросы
- →Почему класс со ссылочным полем не имеет default-конструктора?
- →Может ли ссылка быть
nullptr? Почему?
MiddleТеорияИногдаЧто такое переполнение стека? Причины и предотвращение.
Что такое переполнение стека? Причины и предотвращение.
Происходит, когда стек вызовов превышает лимит (обычно 1–8 МБ); ОС обнаруживает это через guard page и шлёт SIGSEGV. Частые причины: глубокая или бесконечная рекурсия, большие буферы на стеке. Предотвращение: ограничить рекурсию, заменить итерацией, перенести крупные буферы в кучу.
Типичные ошибки
- ✗Объявлять большой массив на стеке в рекурсивной функции — каждый фрейм вызова умножает выделение
- ✗Не добавлять базовый случай в рекурсивную функцию — тривиальная бесконечная рекурсия
- ✗Рассчитывать на оптимизацию хвостового вызова компилятора для обработки неограниченной рекурсии — C++ не гарантирует TCO
Уточняющие вопросы
- →Как увеличить размер стека на Linux и когда это стоит делать?
- →Что такое trampolined recursion и как она избегает переполнения стека?
SeniorКодИногдаРеализуйте пул памяти фиксированного размера с использованием placement new
Реализуйте пул памяти фиксированного размера с использованием placement new
Выделите сырой выровненный буфер, используйте placement new для конструирования объектов в нём, явно вызывайте деструктор перед повторным использованием слота и никогда не вызывайте delete для указателя, полученного через placement new.
Открыть задачу →Типичные ошибки
- ✗Вызов delete для указателя, полученного через placement new — неопределённое поведение
- ✗Несоблюдение требований выравнивания для хранимого типа
- ✗Забытый явный вызов деструктора перед повторным использованием слота
Уточняющие вопросы
- →Как std::allocator связан с placement new?
- →Для чего нужен std::launder и когда он требуется?
SeniorТеорияИногдаКогда и как работать с сырыми указателями и ручным управлением памятью в современном C++?
Когда и как работать с сырыми указателями и ручным управлением памятью в современном C++?
Ограничивайте сырые new/delete пользовательскими аллокаторами, RAII-обёртками и взаимодействием с C API. Не-владеющие наблюдатели могут оставаться сырыми — они не продлевают lifetime. Правило: владеющий сырой указатель сразу оборачивайте в умный.
Типичные ошибки
- ✗Хранить владеющие сырые указатели в членах класса без явного деструктора — утечка при уничтожении класса
- ✗Передавать сырые указатели, возвращённые C API, напрямую в контейнеры C++ без предварительного преобразования в RAII
- ✗Использовать сырую арифметику указателей вместо
std::span(C++20) для представлений массивов —spanнесёт размер и предотвращает выход за границы
Уточняющие вопросы
- →Что такое
std::spanи как он заменяет пары указатель+размер? - →Когда вы использовали бы
std::unique_ptr<T, CustomDeleter>вместо обычного сырого указателя в обёртке C API?
SeniorТеорияИногдаЗачем нужны std::uninitialized_copy / uninitialized_fill и когда писать их самому?
Зачем нужны std::uninitialized_copy / uninitialized_fill и когда писать их самому?
Они конструируют объекты в сырой памяти через placement new и откатывают частично сконструированные элементы при исключениях. Писать самому — при реализации контейнера: разделение allocate и construct даёт семантику reserve/resize.
Типичные ошибки
- ✗Забыть exception safety — частичная конструкция при throw теряет объекты
- ✗Вызывать деструктор на неинициализированных слотах — UB
- ✗Использовать
std::copyдля заполнения сырой памяти — при присваивании пытается вызвать dtor несуществующих объектов
Уточняющие вопросы
- →Что добавляет
std::allocator_traits::constructсверх placement new? - →Когда применять
std::start_lifetime_as(C++23)?
SeniorТеорияИногдаКакие распространённые уязвимости безопасности в C++? Как они работают?
Какие распространённые уязвимости безопасности в C++? Как они работают?
Распространённые уязвимости C++: переполнение буфера, use-after-free, целочисленное переполнение, format string (printf(user_input)), double-free, гонки TOCTOU. Меры: RAII, умные указатели, ASan.
Типичные ошибки
- ✗Думать, что умные указатели предотвращают все use-after-free — сырой указатель, полученный через
get()наshared_ptr, может стать висячим, еслиshared_ptrуничтожается пока сырой указатель ещё используется - ✗Игнорировать переполнение знакового целого — это UB в C++, а не гарантированное оборачивание; компилятор может оптимизировать, предполагая отсутствие переполнения, что приводит к неожиданному коду
- ✗Доверять
strncpyв добавлении null-терминатора —strncpyНЕ добавляет null если источник >= n байт; используйтеstrlcpyилиsnprintf
Уточняющие вопросы
- →Как ASLR (рандомизация адресного пространства) снижает эксплуатацию переполнения буфера?
- →Что такое Control Flow Integrity (CFI) и как Clang реализует её?
MiddleТеорияРедкоКак выделить память с заданным выравниванием в C++?
Как выделить память с заданным выравниванием в C++?
Используйте alignas(N) T x; для стека/static, std::aligned_alloc(N, size) с free (C++17) или operator new(size, std::align_val_t{N}) с соответствующим operator delete (C++17). Обычный new T выравнивает только по alignof(T).
Типичные ошибки
- ✗Использовать
aligned_alloc, затемdelete— нуженfree - ✗Аллоцировать через
newover-aligned тип до C++17 — выравнивание не гарантировано - ✗Путать
alignasиalignof—alignasзадаёт,alignofспрашивает
Уточняющие вопросы
- →Почему SIMD часто требует 16-, 32- или 64-байтное выравнивание?
- →Как
std::pmr::polymorphic_allocatorработает с выравниванием?
SeniorТеорияРедкоКогда и как перегружают operator new / operator delete?
Когда и как перегружают operator new / operator delete?
Перегружают их ради пула мелких объектов, отслеживания выделений или контроля выравнивания. Определяют согласованной парой, членом класса или глобально; operator new принимает size_t и возвращает void* (бросая bad_alloc либо noexcept для nothrow-формы). Членская версия используется new T для этого класса и его наследников.
Типичные ошибки
- ✗Перегрузить
operator new, забыв парныйoperator delete, включая array-формы - ✗Путать
operator new(сырая память) с выражениемnew(память плюс конструирование) - ✗Забывать, что членский
operator deleteвызывается и при удалении наследника через указатель на базу
Уточняющие вопросы
- →Чем отличается array-форма
operator new[]и почему её аргумент размера коварен? - →Почему классовый
operator deleteдолжен бытьstaticи желательно приниматьsize_t?
SeniorТеорияРедкоКакую проблему решает std::pmr::polymorphic_allocator по сравнению с аллокаторами-параметрами шаблона?
Какую проблему решает std::pmr::polymorphic_allocator по сравнению с аллокаторами-параметрами шаблона?
Классические аллокаторы — это параметры шаблона, поэтому vector<int,A> и vector<int,B> — разные типы, которые нельзя смешивать. polymorphic_allocator хранит указатель memory_resource*, выбираемый в рантайме, поэтому стратегия выделения меняется без смены типа контейнера и без раздувания шаблонов.
Типичные ошибки
- ✗Ожидать диспетчеризацию на этапе компиляции — вызовы
memory_resourceвиртуальны в рантайме - ✗Считать
pmr::vectorтем же типом, что иstd::vector— этоvector<T, pmr::polymorphic_allocator<T>> - ✗Допускать, чтобы
monotonic_buffer_resourceпережил свой буфер или использовался сверх ёмкости
Уточняющие вопросы
- →Когда выбрать
monotonic_buffer_resourceвместоunsynchronized_pool_resource? - →Почему
polymorphic_allocatorне пропагируется при копировании или move-присваивании контейнера?
SeniorТеорияРедкоКак realloc используется в реализациях контейнеров? Подводные камни.
Как realloc используется в реализациях контейнеров? Подводные камни.
Контейнеры C++ НЕ используют realloc, поскольку он только побайтово копирует и не вызывает move-конструкторы или обновляет самореференцирующие указатели. std::vector выделяет новый блок, перемещает каждый элемент (или копирует, если move не noexcept), затем уничтожает оригиналы.
Типичные ошибки
- ✗Использовать
reallocв пользовательском контейнере C++ для не-тривиальных типов — конструкторы перемещения и деструкторы обходятся - ✗Не учитывать самореференцирующие типы (например, тип, хранящий указатель на собственный член) — байтовое копирование нарушает инвариант
- ✗Путать
std::vector::reserve(только выделяет) с перевыделением приpush_back(выделяет и перемещает)
Уточняющие вопросы
- →Что такое SBO (small buffer optimisation) в
std::stringи почему оно конфликтует сrealloc? - →Как предложение P1144
trivially_relocatableможет позволить контейнерам безопасно использоватьreallocв будущем?