Как оператор new в C++ понимает, сколько памяти выделять

«Как оператор new в C++ понимает, сколько памяти выделять» — вопрос из категории Управление памятью, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Оператор new в C++ не «понимает» размер динамически. Размер вычисляется на этапе компиляции на основе типа и передается в низкоуровневую функцию выделения памяти.

Механизм работы new T:

  1. Компилятор вычисляет sizeof(T) с учетом выравнивания (alignof(T)).
  2. Генерируется вызов глобальной функции void* operator new(std::size_t count), куда передается вычисленный размер.
  3. Эта функция запрашивает count байт у менеджера памяти ОС (например, через malloc или системный вызов).
  4. Если тип T имеет нетривиальный конструктор, компилятор затем вызывает его для инициализации памяти.

Пример для массива new T[n]:

MyClass* arr = new MyClass[10];

Компилятор вычислит общий размер как: total_size = sizeof(MyClass) * 10 + возможный_оверхед. Оверхед может потребоваться для хранения размера массива (чтобы delete[] знал, сколько деструкторов вызвать).

На практике:

int* p1 = new int;           // Выделяет sizeof(int) байт (например, 4)
double* p2 = new double[5];  // Выделяет 5 * sizeof(double) байт (например, 5*8=40)

struct alignas(16) Vec4 { float x, y, z, w; };
Vec4* vec = new Vec4; // Выделяет 16 байт с выравниванием 16

Важные детали:

  • Размер известен компилятору: sizeof — это оператор времени компиляции.
  • Выравнивание: Память выделяется с учетом alignof(T). Неправильное выравнивание может привести к падению производительности (на x86) или к ошибке (на ARM).
  • Placement new: new (buffer) T() — не выделяет память, а только конструирует объект в предоставленном буфере buffer. Размер типа все равно известен компилятору.
  • Пользовательский operator new: Можно переопределить, чтобы управлять выделением, но ему все равно передается готовый размер.