Динамическое выделение памяти

В C++ существует два основных способа выделения памяти:

  1. Статическое выделение - память выделяется во время компиляции (стек)

  2. Динамическое выделение - память выделяется во время выполнения программы (куча)

Динамическая память управляется операторами new и delete.


Оператор new

Оператор new выделяет память в куче и возвращает указатель на выделенную область.

Выделение памяти для примитивных типов
int *ptr = new int; // Выделяем память для одного целого числа *ptr = 42; // Записываем значение в выделенную память
Выделение памяти с инициализацией
int *ptr = new int(42); // Выделяем память и сразу инициализируем значением 42
Выделение памяти для массивов
int *arr = new int[10]; // Выделяем память для массива из 10 целых чисел arr[0] = 1; // Доступ к элементам массива

Оператор delete

Оператор delete освобождает память, выделенную оператором new.

Освобождение памяти для примитивных типов
int *ptr = new int; // ... использование ptr ... delete ptr; // Освобождаем выделенную память ptr = nullptr; // Хорошая практика - обнулять указатель после удаления
Освобождение памяти для массивов
int *arr = new int[10]; // ... использование массива ... delete[] arr; // Важно использовать delete[] для массивов! arr = nullptr;

Опасности динамической памяти

  1. Утечки памяти - если не освободить память с помощью delete

    int *ptr = new int; // Забыли вызвать delete ptr - утечка памяти!
  2. Двойное удаление

    int *ptr = new int; delete ptr; delete ptr; // Ошибка! Попытка удалить уже освобожденную память
  3. Висячие указатели

    int *ptr = new int; int *ptr2 = ptr; delete ptr; *ptr2 = 10; // Опасное поведение! ptr2 теперь висячий указатель

Умные указатели

В современном C++ рекомендуется использовать умные указатели вместо raw pointers:

  • std::unique_ptr - эксклюзивное владение

  • std::shared_ptr - разделяемое владение

  • std::weak_ptr - слабая ссылка

Пример:
#include <memory> std::unique_ptr<int> ptr = std::make_unique<int>(42); // Память автоматически освободится при выходе из области видимости

Примеры

Динамический массив
#include <iostream> int main() { int size; std::cout << "Введите размер массива: "; std::cin >> size; // Динамическое выделение массива int *arr = new int[size]; // Заполнение массива for (int i = 0; i < size; ++i) { arr[i] = i * 10; } // Вывод массива for (int i = 0; i < size; ++i) { std::cout << arr[i] << " "; } // Освобождение памяти delete[] arr; return 0; }

Пояснение: Пользователь вводит размер массива, который выделяется в куче. После использования память освобождается.

Двумерный динамический массив
#include <iostream> int main() { int rows, cols; std::cout << "Введите количество строк и столбцов: "; std::cin >> rows >> cols; // Создание массива указателей на строки int **matrix = new int*[rows]; // Выделение памяти для каждой строки for (int i = 0; i < rows; ++i) { matrix[i] = new int[cols]; } // Заполнение и вывод матрицы for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { matrix[i][j] = i + j; std::cout << matrix[i][j] << " "; } std::cout << std::endl; } // Освобождение памяти for (int i = 0; i < rows; ++i) { delete[] matrix[i]; } delete[] matrix; return 0; }

Пояснение: Создается двумерный массив, где сначала выделяется массив указателей, а затем для каждого указателя выделяется массив значений. Освобождение происходит в обратном порядке.


Рекомендации по работе с динамической памятью

  1. Всегда проверяйте, что new вернул не-nullptr (в старых стандартах)

  2. Используйте delete для одиночных объектов и delete[] для массивов

  3. После delete устанавливайте указатель в nullptr

  4. По возможности используйте умные указатели вместо raw pointers

  5. Избегайте "голого" new и delete в высокоуровневом коде

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


Комментарии

Добавить комментарий

Чтобы оставить комменатрий необходимо Авторизоваться