« Zarządzanie pamięcią new, delete, lekcja »
Rozdział PTR+1. Lekcja przybliża działanie operatorów do zarządzana pamięcią, new i delete (lekcja)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
Autor: pekfos
Kurs C++

Zarządzanie pamięcią new, delete

[lekcja] Rozdział PTR+1. Lekcja przybliża działanie operatorów do zarządzana pamięcią,
new
 i
delete

Wstęp

W czasie lektury poprzedniej lekcji, być może zastanawiałeś się, po co w ogóle te wskaźniki? Nie wydają się wprowadzać niczego nowego, bo można równie dobrze używać zmiennych po ich nazwach, a nie adresach. Arytmetyka wskaźników to fajna opcja, ale ma sens tylko na tablicach, a te można indeksować i na jedno wychodzi. W tej lekcji omówiony będzie sposób na uzyskanie pamięci, do której nie można się odnieść po nazwie.

new i delete

new jest operatorem dynamicznej alokacji pamięci. Zwraca adres lokalizacji pamięci, której możemy od teraz używać do przechowywania wartości jakiegoś typu.
C/C++
int * wsk = new int;
* wsk = 123;
Zrób "nowy int" i zapisz adres. Tak utworzona zmienna będzie istnieć tak długo, aż zostanie ręcznie zwolniona, lub program się zakończy. Do zwolnienia pamięci służy operator delete
C/C++
delete wsk;
Po wykonaniu tej instrukcji, wartość wskaźnika wsk pozostaje bez zmiany, ale nie można już robić niczego z pamięcią pod tym adresem - nie jest już nasza. Jak pewnie pamiętasz z lekcji o wskaźnikach, błędna niezerowa wartość wskaźnika jest nieodróżnialna od poprawnej, więc dobrą praktyką jest wyzerowanie wskaźnika po usunięciu pamięci:
C/C++
delete wsk;
wsk = nullptr;
Każda zaalokowana pamięć powinna zostać zwolniona. Jeśli program w jakimś przypadku nie zwalnia pamięci, mamy wówczas do czynienia z wyciekiem pamięci (memory leak). Niezwalnianie pamięci może doprowadzić do wyczerpania zasobów komputera, a wtedy będzie problem. Nie da się stwierdzić, czy jakaś pamięć jest nieużywana i potrzebna, czy nieużywana i niepotrzebna, bo jakiś niezdarny programista zgubił jej adres.

new[] i delete[]

Wszystko super z tym new, ale tyle zachodu dla utworzenia jednego inta to słaby deal. Jak trzeba utworzyć zmienną na wskaźnik, to czemu po prostu nie utworzyć zmiennej? Alokacja jednej zmiennej ma swoje istotne zastosowania, które wkrótce poznasz, ale na razie bardziej przydatna byłaby możliwość utworzenia całej tablicy zmiennych. Od tego jest operator new[].
C/C++
int * tab = new int[ 100 ];
tab[ 42 ] = 123;
To ma kilka dużych zalet względem zwykłej tablicy int tab[100]:
  • Rozmiar nie musi być znany z góry (użycie zmiennej jako rozmiaru w zwykłej tablicy nie należy do standardu C++)
  • Tablicę można powiększyć (dokładniej: można utworzyć drugą większą tablicę, skopiować dane i usunąć poprzednią)
  • Tablica może być znacznie większa
Do usunięcia tablicy należy użyć delete[]:
C/C++
delete[] tab;
Do delete/delete[] można bezpiecznie podać pusty wskaźnik i nie powoduje to zwolnienia żadnej pamięci
new int i new int[1] nie są równoważne. Alokacja jednej zmiennej nie jest alokacją jednoelementowej tablicy, więc delete[] nie jest "uniwersalniejszą" wersją delete. delete i delete[] nie mogą być stosowane zamiennie.

Przykład - powiększająca się tablica

C/C++
#include <iostream>


int main()
{
    int * tablica = nullptr, rozmiar = 0;
    std::cout << "Podawaj liczby, 0 konczy wczytywanie.\n";
   
    while( true )
    {
        int liczba;
        std::cin >> liczba;
       
        if( liczba == 0 )
             break;
       
        // Brakuje miejsca, utwórz większą tablicę
        int * nowa = new int[ rozmiar + 1 ];
       
        // Skopiuj dane
        for( int i = 0; i < rozmiar; ++i )
             nowa[ i ] = tablica[ i ];
       
        // Dodaj nową wartość
        nowa[ rozmiar ] = liczba;
       
        // Usuń starą tablicę
        delete[] tablica;
       
        // Zakutalizuj zmienne
        tablica = nowa;
        rozmiar++;
    }
   
    std::cout << "Te same liczby, ale odwrotnie!\n";
   
    for( int i = rozmiar - 1; i >= 0; --i )
         std::cout << tablica[ i ] << ' ';
   
    delete[] tablica;
}
Zwróć uwagę, że rozmiar równe zero na początku programu sprawia, że program nigdy nie odwołuje się do nieistniejącej pamięci.

Zadanie domowe

Zmodyfikuj przykładowy kod tak, aby nowa tablica nie była tworzona za każdym razem, gdy dodawany jest nowy element.