Динамическая память (new/delete)
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 14 из 104.
Теперь, когда вы понимаете указатели и ссылки, давайте применим их на практике с динамическим выделением памяти. Операторы new и delete позволяют создавать и удалять объекты в куче во время выполнения программы.
Используйте new для выделения памяти и получения указателя на неё:
int* num = new int; // Выделение памяти под одно целое число
*num = 42; // Присваивание значения через указатель
Player* player = new Player("Hero"); // Выделение памяти под объектКаждый new должен иметь соответствующий delete для освобождения памяти:
delete num; // Освобождение одного объекта
delete player; // Вызывает деструктор, затем освобождает памятьДля массивов используйте new[] и delete[]:
int* scores = new int[5]; // Выделение памяти под массив из 5 целых чисел
scores[0] = 100;
delete[] scores; // Освобождение памяти массива — обратите внимание на скобки!Распространенные ошибки, которых следует избегать:
// Утечка памяти — забыли вызвать delete
int* ptr = new int(10);
ptr = new int(20); // Потерян доступ к первому выделению памяти!
// Висячий указатель — использование после удаления
delete ptr;
*ptr = 5; // Неопределенное поведение!
// Несоответствующий delete
int* arr = new int[10];
delete arr; // Ошибка! Должно быть delete[]После удаления устанавливайте указатели в nullptr, чтобы избежать случайного повторного использования. Ручное управление памятью подвержено ошибкам, поэтому в современном C++ предпочтительнее использовать умные указатели, которые мы изучим далее.
Задание
ЛегкоДавайте создадим простую систему хранения сообщений, которая демонстрирует динамическое выделение памяти с помощью new и delete.
Вы создадите два файла для организации вашего кода:
MessageBox.h: Определите классMessageBox, который динамически управляет массивом строк. Класс должен содержать:- Приватный указатель на динамически выделенный массив строк
- Приватную целую переменную для отслеживания текущего количества сообщений
- Приватную целую переменную для хранения максимальной емкости
- Конструктор, который принимает емкость и выделяет массив с помощью
new[] - Деструктор, который правильно освобождает память с помощью
delete[]и выводит"MessageBox destroyed" - Метод
addMessage(string msg), который добавляет сообщение, если есть место, возвращаяtrueв случае успеха илиfalse, если массив заполнен - Метод
printAll(), который выводит каждое сохраненное сообщение на новой строке
main.cpp: Считайте значение емкости и количество сообщений из входных данных. Затем считайте соответствующее количество строк сообщений. Создайте объектMessageBoxв куче с помощьюnew, добавьте в него все сообщения, вызовитеprintAll(), а затем правильно удалите MessageBox, чтобы освободить память.
Формат входных данных:
- Первая строка: емкость (целое число)
- Вторая строка: количество добавляемых сообщений (целое число)
- Следующие строки: по одному сообщению на строку
Помните: каждому new должен соответствовать delete, а каждому new[] — delete[]. После удаления рекомендуется устанавливать указатели в nullptr.
Включите ваш заголовочный файл в main.cpp с помощью #include "MessageBox.h".
Шпаргалка
Используйте new для динамического выделения памяти в куче и получения указателя на неё:
int* num = new int; // Allocate single integer
*num = 42; // Assign value through pointer
Player* player = new Player("Hero"); // Allocate objectДля каждого new должен быть соответствующий delete, чтобы освободить память:
delete num; // Free single object
delete player; // Calls destructor, then frees memoryДля массивов используйте new[] и delete[]:
int* scores = new int[5]; // Allocate array of 5 integers
scores[0] = 100;
delete[] scores; // Free array - note the brackets!Распространенные ошибки, которых следует избегать:
// Memory leak - forgot to delete
int* ptr = new int(10);
ptr = new int(20); // Lost access to first allocation!
// Dangling pointer - using after delete
delete ptr;
*ptr = 5; // Undefined behavior!
// Mismatched delete
int* arr = new int[10];
delete arr; // Wrong! Should be delete[]После удаления устанавливайте указатели в nullptr, чтобы избежать случайного повторного использования.
Попробуйте сами
#include <iostream>
#include <string>
#include "MessageBox.h"
using namespace std;
int main() {
int capacity;
int numMessages;
// Считать вместимость и количество сообщений
cin >> capacity;
cin >> numMessages;
cin.ignore(); // Очистить символ новой строки после числа
// TODO: Создать MessageBox в куче, используя new
// TODO: Прочитать и добавить все сообщения в MessageBox
for (int i = 0; i < numMessages; i++) {
string message;
getline(cin, message);
// Добавить сообщение в MessageBox
}
// TODO: Вызвать printAll(), чтобы отобразить все сообщения
// TODO: Удалить MessageBox, чтобы освободить память
// Не забудьте установить указатель в nullptr после удаления
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер