<MyRusakov.ru />

Разработчик игр на Unreal Engine

Разработчик игр на Unreal Engine

Этот комплект с нуля всего за 7 месяцев сделает Вас Unreal-разработчиком. И при этом учиться достаточно 1 час в день.

Начнёте Вы с основ программирования, постепенно перейдя к C++. Затем очень подробно изучите Unreal Engine, и после научитесь программировать на C++ в Unreal Engine. В конце создадите крупный проект на C++ в Unreal Engine для своего портфолио.

Комплект содержит:

- 416 видеоуроков

- 95 часов видео

- 1024 задания для закрепления материала из уроков

- 3 финальных тестирования

- 4 сертификата

- 12 Бонусных курсов

Подробнее
Подписка

Подпишитесь на мой канал на YouTube, где я регулярно публикую новые видео.

YouTube Подписаться

Подписавшись по E-mail, Вы будете получать уведомления о новых статьях.

Подписка Подписаться

Добавляйтесь ко мне в друзья ВКонтакте! Отзывы о сайте и обо мне оставляйте в моей группе.

Мой аккаунт Мой аккаунт Моя группа
Опрос

Какая тема Вас интересует больше?

Динамическая память в C++: new и delete — практическое руководство с примерами

Динамическая память в C++: new и delete — практическое руководство с примерами

Что такое динамическая память и когда она нужна

Динамическая память (heap) выделяется во время выполнения программы, когда размер данных заранее неизвестен или слишком велик для стека. В C++ для этого используются операторы new и delete (а также их варианты для массивов).

Базовый синтаксис: new и delete

int* p = new int;            // без инициализации (значение не определено)
int* q = new int(42);        // прямая инициализация
int* r = new int();          // value-init: для int даёт 0

std::cout << *q << "\n";   // 42

delete p;                    // освобождаем память одного объекта
delete q;
delete r;

Важно: для встроенных типов new int оставляет значение неинициализированным; используйте new int(), если нужен ноль. Для пользовательских типов всегда вызывается конструктор.

Массивы: new[] и delete[]

int* a = new int[5];                 // элементы неинициализированы
int* b = new int[5]{1,2,3,4,5};      // список инициализации

// ... работаем с a и b

delete[] a;                          // обязательно delete[] для массивов
delete[] b;

Нельзя смешивать new с delete[] и наоборот — это неопределённое поведение. Всегда освобождайте память тем же оператором, которым выделяли.

Конструкторы и деструкторы при динамическом выделении

#include <iostream>

struct Logger {
    Logger(int id) : id(id) { std::cout << "ctor(" << id << ")\n"; }
    ~Logger() { std::cout << "dtor(" << id << ")\n"; }
    int id;
};

int main() {
    Logger* p = new Logger(1);   // вызов конструктора
    delete p;                    // вызов деструктора

    Logger* arr = new Logger[3]{ {10}, {20}, {30} }; // для каждого элемента ctor
    delete[] arr;                                        // для каждого элемента dtor
}

При выделении массива объектов вызывается конструктор для каждого элемента, а при освобождении — соответствующие деструкторы.

Исключения и nothrow

Если памяти не хватает, new бросает std::bad_alloc. Это корректный и безопасный путь обработки ошибок выделения памяти.

#include <new>
#include <iostream>

try {
    int* huge = new int[1'000'000'000];
    delete[] huge;
} catch (const std::bad_alloc& e) {
    std::cerr << "Не удалось выделить память: " << e.what() << "\n";
}

Вариант с nothrow не бросает исключение, а возвращает nullptr при ошибке. Его удобно использовать в местах, где нельзя бросать исключения:

#include <new>
int* data = new (std::nothrow) int[1000000000000ULL];
if (!data) {
    // обработка ошибки: памяти нет
} else {
    delete[] data;
}

Типичные ошибки и как их избежать

  • Утечка памяти: забыли сделать delete или пути выхода сложные.
  • Двойное удаление: вызвать delete дважды для одного указателя — UB.
  • Висячий указатель (dangling pointer): продолжаете использовать указатель после delete.
  • Несоответствие операторов: new с delete[], new[] с delete — UB.
  • Смешивание new/delete и malloc/free: так делать нельзя.
int* p = new int(10);
int* alias = p;        // второй указатель на тот же объект
delete p;              // память освобождена
// *alias = 5;        // ОПАСНО: alias висячий указатель (UB)
p = nullptr;           // смягчает риск повторного delete, но alias всё ещё опасен

Хорошая привычка: после delete обнулять указатель, если он ещё будет виден. Но помните, что копии указателя в других местах останутся висячими — проектируйте владение аккуратно.

Не смешивайте new/delete и malloc/free

int* p = new int(5);
// free(p);            // НЕЛЬЗЯ — повредите аллокатор/метаданные, UB
delete p;              // корректно

int* q = (int*)std::malloc(sizeof(int));
// delete q;           // НЕЛЬЗЯ — освобождать нужно free
std::free(q);

Причина: механизмы управления памятью инициализации/деинициализации у этих семейства функций разные. Для объектов C++ всегда используйте new/delete.

virtual деструктор и удаление через базовый указатель

Если удаляете объект наследника через указатель на базовый класс, деструктор базового класса должен быть виртуальным, иначе деструктор производного не вызовется (утечка/поломка логики).

struct Base { virtual ~Base() = default; };
struct Derived : Base { ~Derived() { /* освобождение ресурсов */ } };

Base* b = new Derived();
delete b; // вызовется ~Derived(), затем ~Base()

Placement new (коротко)

Placement new конструирует объект в уже выделенной области памяти. Удалять такую память оператором delete нельзя; нужно вручную вызвать деструктор и освободить буфер тем способом, которым он был получен.

#include <new>
#include <iostream>

struct S { ~S(){ std::cout << "dtor\n"; } };
alignas(S) unsigned char buf[sizeof(S)];
S* obj = new (buf) S();  // конструируем в buf
obj->~S();               // явно вызываем деструктор
// память buf освобождать не нужно (статический буфер здесь)

Используйте этот приём только при чётком понимании жизненного цикла и выравнивания памяти.

Лучшие практики

  • По возможности используйте автоматические объекты (на стеке) и контейнеры стандартной библиотеки (std::vector, std::string) вместо ручного new/delete.
  • Если нужен динамический объект с владением — в современном коде чаще выбирают умные указатели (std::unique_ptr, std::shared_ptr) — это снижает риск утечек и упрощает код.
  • Для массивов предпочитайте std::vector<T> — безопаснее и удобнее, чем new T[].
  • Обнуляйте указатель после delete, избегайте дублирования владения одним и тем же сырым указателем.
  • Обрабатывайте ошибки выделения: исключения или nothrow — в зависимости от политики проекта.

Мини‑практикум: динамический массив с инициализацией и проверкой ошибок

#include <iostream>
#include <new>

int* make_array(std::size_t n, int init) {
    int* data = new (std::nothrow) int[n];
    if (!data) return nullptr;
    for (std::size_t i = 0; i < n; ++i) data[i] = init;
    return data; // Владелец обязан вызвать delete[]
}

int main() {
    std::size_t n = 5;
    if (int* a = make_array(n, 7)) {
        for (std::size_t i = 0; i < n; ++i) std::cout << a[i] << ' ';
        std::cout << "\n";
        delete[] a; // не забудьте! Иначе утечка
    } else {
        std::cerr << "Память не выделена\n";
    }
}

Обратите внимание на договорённость о владении: кто получил указатель — тот и освобождает. В реальных проектах старайтесь инкапсулировать владение (RAII/умные указатели/контейнеры), чтобы не забывать про delete.

Чек‑лист по new/delete

  • Выделил с new — освободи delete; выделил с new[] — освободи delete[].
  • Не используй указатель после delete; по возможности обнуляй его.
  • Не смешивай new/delete и malloc/free.
  • При удалении через базовый указатель — виртуальный деструктор в базе.
  • Обрабатывай ошибки выделения: исключения или nothrow.

Что дальше изучать

Закрепить материал и перейти к более безопасным техникам управления ресурсами поможет качественный курс. Рекомендую посмотреть программу и попробовать практику из курса: Хочу быстро прокачать C++ на курсе «С Нуля до Гуру» с практикой и обратной связью.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (https://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: https://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: https://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.