Pamięć dynamiczna (new/delete)
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 14 z 104.
Skoro rozumiesz już wskaźniki i referencje, wykorzystajmy je do dynamicznej alokacji pamięci. Operatory new i delete pozwalają tworzyć i niszczyć obiekty na stercie w czasie działania programu.
Użyj new, aby przydzielić pamięć i otrzymać wskaźnik do niej:
int* num = new int; // Przydziel pamięć na pojedynczą liczbę całkowitą
*num = 42; // Przypisz wartość za pomocą wskaźnika
Player* player = new Player("Hero"); // Przydziel pamięć na obiektKażde new musi mieć odpowiadające mu delete, aby zwolnić pamięć:
delete num; // Zwalnia pojedynczy obiekt
delete player; // Wywołuje destruktor, a następnie zwalnia pamięćW przypadku tablic użyj new[] i delete[]:
int* scores = new int[5]; // Przydziel pamięć na tablicę 5 liczb całkowitych
scores[0] = 100;
delete[] scores; // Zwolnij pamięć tablicy — zwróć uwagę na nawiasy kwadratowe!Typowe błędy, których należy unikać:
// Wyciek pamięci – zapomniano użyć delete
int* ptr = new int(10);
ptr = new int(20); // Utracono dostęp do pierwszego przydzielonego obszaru pamięci!
// Wiszący wskaźnik – użycie po delete
delete ptr;
*ptr = 5; // Niezdefiniowane zachowanie!
// Niedopasowane delete
int* arr = new int[10];
delete arr; // Źle! Należy użyć delete[]Po usunięciu ustaw wskaźniki na nullptr, aby uniknąć ich przypadkowego ponownego użycia. Ręczne zarządzanie pamięcią jest podatne na błędy, dlatego nowoczesny C++ preferuje inteligentne wskaźniki, które omówimy w następnej kolejności.
Wyzwanie
ŁatwyZbudujmy prosty system przechowywania wiadomości, który demonstruje dynamiczną alokację pamięci za pomocą new i delete.
Utworzysz dwa pliki, aby uporządkować swój kod:
MessageBox.h: Zdefiniuj klasęMessageBox, która dynamicznie zarządza tablicą wiadomości. Klasa powinna zawierać:- Prywatny wskaźnik do dynamicznie zaalokowanej tablicy ciągów znaków
- Prywatną liczbę całkowitą śledzącą bieżącą liczbę wiadomości
- Prywatną liczbę całkowitą przechowującą maksymalną pojemność
- Konstruktor, który przyjmuje pojemność i alokuje tablicę za pomocą
new[] - Destruktor, który prawidłowo zwalnia pamięć za pomocą
delete[]i wyświetla"MessageBox destroyed" - Metodę
addMessage(string msg), która dodaje wiadomość, jeśli jest miejsce, zwracająctruew przypadku powodzenia lubfalse, jeśli tablica jest pełna - Metodę
printAll(), która wyświetla każdą przechowywaną wiadomość w osobnym wierszu
main.cpp: Wczytaj z wejścia wartość pojemności i liczbę wiadomości. Następnie wczytaj tyle ciągów znaków z wiadomościami. Utwórz obiektMessageBoxna stercie za pomocąnew, dodaj do niego wszystkie wiadomości, wywołajprintAll(), a następnie prawidłowo usuń obiekt MessageBox, aby zwolnić pamięć.
Format danych wejściowych będzie następujący:
- Pierwszy wiersz: pojemność (liczba całkowita)
- Drugi wiersz: liczba wiadomości do dodania (liczba całkowita)
- Kolejne wiersze: jedna wiadomość w każdym wierszu
Pamiętaj: każdemu new musi odpowiadać pasujące delete, a każdemu new[] musi odpowiadać pasujące delete[]. Po usunięciu dobrze jest ustawić wskaźniki na nullptr.
Dołącz plik nagłówkowy w main.cpp za pomocą #include "MessageBox.h".
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "MessageBox.h"
using namespace std;
int main() {
int capacity;
int numMessages;
// Wczytaj pojemność i liczbę wiadomości
cin >> capacity;
cin >> numMessages;
cin.ignore(); // Usuń znak nowej linii po liczbie
// TODO: Utwórz MessageBox na stercie za pomocą new
// TODO: Wczytaj i dodaj wszystkie wiadomości do MessageBox
for (int i = 0; i < numMessages; i++) {
string message;
getline(cin, message);
// Dodaj wiadomość do MessageBox
}
// TODO: Wywołaj printAll(), aby wyświetlić wszystkie wiadomości
// TODO: Usuń MessageBox, aby zwolnić pamięć
// Pamiętaj, aby po usunięciu ustawić wskaźnik na nullptr
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online