Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает оператор new в C++
Оператор new — это фундаментальная операция для динамического выделения памяти на heap. Он состоит из трёх основных этапов: выделение памяти, вызов конструктора и возврат указателя.
Базовый синтаксис
int* ptr = new int; // Выделить памяти для int
int* arr = new int[100]; // Выделить массив из 100 int
MyClass* obj = new MyClass(); // Выделить и инициализировать
MyClass* obj2 = new MyClass(10, 20); // С параметрами
Три шага работы new
Шаг 1: Выделение памяти (malloc или эквивалент)
int* ptr = new int;
// 1. Выделяется 4 или 8 байт на heap
malloc(sizeof(int))
Шаг 2: Вызов конструктора (если это объект)
MyClass* obj = new MyClass(42);
// 2. Конструктор MyClass::MyClass(42) вызывается
// на адресе выделенной памяти
Шаг 3: Возврат указателя
// 3. Возвращается указатель на выделенную память
Как это выглядит под капотом
// Упрощённо, new работает примерно так:
template<typename T>
T* operator_new()
{
// 1. Запросить память
void* raw_memory = malloc(sizeof(T));
if (!raw_memory) {
throw std::bad_alloc(); // Если не хватает памяти
}
// 2. Вызвать конструктор (placement new)
T* object = new (raw_memory) T();
// 3. Вернуть указатель
return object;
}
// Вызов:
int* ptr = operator_new<int>(); // Эквивалент: new int
Выделение памяти для примитивов
int* ptr = new int; // Выделить, не инициализировать
int* ptr = new int(); // Выделить, инициализировать нулём
int* ptr = new int(42); // Выделить, инициализировать 42
float* f = new float{3.14}; // C++11 uniform initialization
Выделение памяти для объектов
class MyClass {
public:
int x;
std::string name;
MyClass() : x(0), name("") {
std::cout << "Default constructor" << std::endl;
}
MyClass(int x, std::string n) : x(x), name(n) {
std::cout << "Parameterized constructor" << std::endl;
}
~MyClass() {
std::cout << "Destructor" << std::endl;
}
};
int main() {
MyClass* obj1 = new MyClass(); // Default constructor
MyClass* obj2 = new MyClass(42, "Alice"); // Parameterized
delete obj1; // Вызывает деструктор
delete obj2; // Вызывает деструктор
}
Выделение массивов
int* arr = new int[100]; // Массив из 100 int
// Важно: нужно удалять с []
delete[] arr; // Правильно
// delete arr; // НЕПРАВИЛЬНО! Memory leak
// Для объектов:
MyClass* objects = new MyClass[50];
delete[] objects; // Вызывает деструктор для всех 50 объектов
Где располагается память
int local = 5; // Stack (автоматическое удаление)
int* heap = new int(5); // Heap (ручное удаление)
static int global = 5; // Global (удаляется при выходе из программы)
Stack vs Heap:
Stack: Heap:
- Быстро - Медленнее
- Ограниченный размер (~1-8MB) - Большой размер (ГБ)
- Автоматическое удаление - Ручное удаление
- Жёсткая структура (LIFO) - Гибкая структура
Обработка ошибок
// Если памяти не хватает, new выбросит исключение
try {
int* arr = new int[1000000000]; // Слишком много
} catch (std::bad_alloc& e) {
std::cerr << "Memory allocation failed: " << e.what() << std::endl;
}
// Старый style (C-style):
int* ptr = new (nothrow) int[1000000000];
if (ptr == nullptr) {
std::cerr << "Allocation failed" << std::endl;
}
Правильное использование: RAII
// ПЛОХО: ручное управление памятью
void function() {
MyClass* obj = new MyClass();
// Если это выброшет исключение, memory leak!
if (something_wrong) throw std::runtime_error("Error");
delete obj; // Может не выполниться
}
// ХОРОШО: умные указатели (RAII)
void function() {
std::unique_ptr<MyClass> obj(new MyClass());
if (something_wrong) throw std::runtime_error("Error");
// obj автоматически удалится при выходе из scope
}
// ЕЩЁ ЛУЧШЕ: make_unique
void function() {
auto obj = std::make_unique<MyClass>();
// obj удалится автоматически
}
Перегрузка operator new
class MyClass {
public:
// Перегружаем new для класса
void* operator new(size_t size) {
std::cout << "Custom new: " << size << " bytes" << std::endl;
return malloc(size);
}
void operator delete(void* ptr) {
std::cout << "Custom delete" << std::endl;
free(ptr);
}
};
int main() {
MyClass* obj = new MyClass(); // Вызывает custom new
delete obj; // Вызывает custom delete
}
Placement new (продвинутая техника)
// Конструктор вызывается на уже выделенной памяти
char buffer[sizeof(MyClass)];
MyClass* obj = new (buffer) MyClass(42); // Placement new
// Объект создан на stack-выделенном буфере!
// Деструктор нужно вызвать вручную:
obj->~MyClass();
// Но НЕ delete obj (не выделял через new)
Производительность
// new + delete имеют overhead
int* ptr = new int; // ~50-100 циклов на выделение
delete ptr; // ~30-50 циклов на удаление
// Stack allocation значительно быстрее
int x = 5; // ~1 цикл
// Для критичного по производительности кода:
std::vector<int> pool; // Пул предвыделённой памяти
pool.resize(1000);
// Потом переиспользуем существующую память
Практический пример
#include <iostream>
#include <memory>
class Person {
public:
std::string name;
int age;
Person(const std::string& n, int a) : name(n), age(a) {
std::cout << "Person created: " << name << std::endl;
}
~Person() {
std::cout << "Person destroyed: " << name << std::endl;
}
};
int main() {
// OLD (manual management)
Person* p1 = new Person("Alice", 30);
// ...
delete p1; // Ручное удаление
// NEW (RAII с unique_ptr)
{
auto p2 = std::make_unique<Person>("Bob", 25);
// p2 удалится автоматически при выходе из scope
} // ~Person() вызывается здесь
// NEW (RAII с shared_ptr для множественного владения)
std::shared_ptr<Person> p3 = std::make_shared<Person>("Charlie", 35);
{
std::shared_ptr<Person> p4 = p3; // Shared ownership
// p3 и p4 указывают на один объект
} // p4 удаляется, но объект остаётся (p3 всё ещё владеет)
return 0;
} // p3 удаляется здесь
Итог
new работает в 3 шага:
- Выделить память на heap (malloc)
- Вызвать конструктор на выделенной памяти
- Вернуть указатель на объект
Важные правила:
- new → delete (pairing)
- new[] → delete[] (для массивов)
- Используй умные указатели (unique_ptr, shared_ptr)
- Избегай ручного управления памятью
- Помни о RAII принципе
Современный C++ (C++11+) предпочитает умные указатели вместо ручного new/delete.