Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ: new - оператор динамического выделения памяти
Оператор new в C++ выделяет память в heap (динамическую память) и возвращает указатель на неё. Это основной способ работы с объектами переменного размера.
Как работает new
Шаг 1: Выделение памяти
int* ptr = new int; // Выделяет 4 байта в heap
int* array = new int[100]; // Выделяет 400 байт в heap
MyClass* obj = new MyClass(); // Выделяет sizeof(MyClass) байт
Оператор new:
- Вызывает operator new(size) - выделяет место в heap
- Вызывает конструктор (если это класс)
- Возвращает указатель на выделенную память
Шаг 2: Использование
int* ptr = new int(42); // Выделил и инициализировал
std::cout << *ptr << std::endl; // Разыменовываем, 42
std::cout << ptr[0] << std::endl; // Массив
delete ptr; // Освобождаем память
ptr = nullptr; // ❌ Не автоматически!
Сравнение со стеком
// На стеке (автоматическая память)
{
int x = 5; // stack: создаётся
MyClass obj; // stack: вызывается конструктор
} // Выходим из scope: деструктор, память освобождена
// На heap (динамическая память)
{
int* ptr = new int(5); // heap: выделяется
MyClass* obj = new MyClass(); // heap: вызывается конструктор
} // Выходим из scope: УТЕЧКА ПАМЯТИ!
// delete забыли вызвать
Под капотом
Память heap
Виртуальное адресное пространство процесса:
┌─────────────────────┐
│ Text (код) │ 0x00000000
├─────────────────────┤
│ Data (инициализ.) │
├─────────────────────┤
│ BSS (неинициализ.) │
├─────────────────────┤
│ Heap ↓ │ new выделяет отсюда
│ [метаданные][данные][свободно]
├─────────────────────┤
│ ... │ промежуток
├─────────────────────┤
│ Stack ↑ │
│ [локальные переменные]
│ [return addresses] │
│ [параметры функций]│
└─────────────────────┘ 0xFFFFFFFF
Этот промежуток заполняется в обе стороны: heap растёт вверх, stack вниз.
Внутренние метаданные
// Когда вызываем new int[100], БД создаёт:
struct AllocationBlock {
size_t size; // 400 байт (для 100 ints)
void* prev; // Указатель на предыдущий блок
void* next; // Указатель на следующий блок
// ...
};
// Память в heap:
┌──────────────────┐
│ [метаданные] │ ← указатель, который вернёт new
│ [100 ints] │
│ [метаданные] │ следующего блока
│ [другие данные] │
└──────────────────┘
При delete нужно знать размер, он хранится в метаданных.
Типы new
1. new для одного объекта
MyClass* obj = new MyClass();
// Вызывает конструктор
delete obj; // Вызывает деструктор
2. new для массива
MyClass* arr = new MyClass[10];
// Вызывает конструктор для каждого элемента
delete[] arr; // Вызывает деструктор для каждого
❌ ВАЖНО: delete vs delete[]
int* single = new int(5);
int* array = new int[5];
delete single; // OK
delete[] array; // OK
// Если перепутаешь:
delete array; // ❌ Undefined behavior! Может быть утечка
delete[] single; // ❌ Undefined behavior!
3. Размещающий new (Placement new)
char buffer[sizeof(MyClass)];
MyClass* obj = new(buffer) MyClass(); // Выделяет в buffer!
// Деструктор вызываем вручную
obj->~MyClass();
// delete НЕ вызываем!
Используется когда нужна память в конкретном месте.
Оператор new можно переопределить
class MyClass {
public:
void* operator new(size_t size) {
std::cout << "Allocating " << size << " bytes" << std::endl;
return malloc(size);
}
void operator delete(void* ptr) {
std::cout << "Freeing memory" << std::endl;
free(ptr);
}
};
MyClass* obj = new MyClass(); // Вызовет operator new
delete obj; // Вызовет operator delete
Современный подход: smart pointers
❌ Старый способ
MyClass* obj = new MyClass(); // ручное управление
// ... множество кода ...
if (error) {
return; // Забыли delete!
}
delete obj; // может не выполниться
✅ Новый способ (C++11+)
std::unique_ptr<MyClass> obj = std::make_unique<MyClass>();
// obj.get() - получить сырой указатель
// obj.reset() - удалить и выделить новое
} // Автоматически delete
std::shared_ptr<MyClass> shared = std::make_shared<MyClass>();
} // Автоматически delete (reference counting)
Исключения при new
try {
int* huge = new int[1000000000000]; // Много памяти
} catch (const std::bad_alloc& e) {
std::cerr << "Memory allocation failed: " << e.what() << std::endl;
}
// Или nothrow версия
int* ptr = new(std::nothrow) int[1000000000000];
if (ptr == nullptr) {
std::cerr << "Allocation failed" << std::endl;
}
Производительность
// ❌ Медленно: много маленьких выделений
for (int i = 0; i < 1000000; ++i) {
int* x = new int(i); // 1 миллион new операций
// Работаем
delete x; // 1 миллион delete операций
}
// ✅ Быстро: одно большое выделение
std::vector<int> data;
data.reserve(1000000); // Одно выделение
for (int i = 0; i < 1000000; ++i) {
data.push_back(i);
}
// Автоматическое удаление при выходе из scope
Утечки памяти
// ❌ Утечка
MyClass* obj = new MyClass();
do_something(obj);
// delete забыли!
// ❌ Утечка в исключении
void function() {
MyClass* obj = new MyClass();
dangerous_operation(); // Может выкинуть исключение
delete obj; // Может не выполниться
}
// ✅ RAII
void function() {
std::unique_ptr<MyClass> obj = std::make_unique<MyClass>();
dangerous_operation(); // Выкинет исключение? obj уничтожится автоматически
}
Инструменты для debug
# Valgrind - найти утечки
valgrind --leak-check=full ./program
# AddressSanitizer (ASan)
g++ -fsanitize=address program.cpp
./a.out # Покажет утечки
# Clang memory profiler
clang++ -fprofile-instr-generate program.cpp
Итог
✅ new выделяет память в heap ✅ Возвращает указатель на выделенную память ✅ delete освобождает память ✅ delete[] для массивов (не забудь!) ✅ Smart pointers (unique_ptr, shared_ptr) - автоматическое управление ⚠️ Забыл delete = утечка памяти ⚠️ double delete = undefined behavior ⚠️ delete [] vs delete - важно различать! ⚠️ Старайся избегать сырых new/delete - используй RAII