Programowanie w języku C++
Programowanie w języku C++
Obiekty a zarządzanie pamięcią
Część siódma Roman Simiński roman.siminski@us.edu.pl www.us.edu.pl/~siminski Autor Kontakt
Pojęcie sterty
Pojęcie sterty ― heap― heap Sterta
Sterta (ang.heap) to wydzielony obszar pamięci:
przeznaczony do przechowywania danych dynamicznych,
kontrolowany ręcznie przez programistę,
ograniczony pod względem rozmiaru.
Typowy scenariusz wykorzystania sterty:
przydział niezbędnej w danym momencie ilości pamięci — najpóźniej jak to
możliwe,
wykorzystanie przydzielonego obszaru,
zwolnienie przydzielonej pamięci natychmiast, gdy nie jest już ona
potrzebna.
Rozmiar sterty zmienia się wraz ze zmianą środowiska systemowego i kompilatora. Zwykle jest on jednak wystarczający dla typowych programów. Jednak przetwarzanie grafiki czy danych multimedialnych może wymagać ustawień specyficznych.
Dynamiczny przydział pamięci w językach C i C++
Dynamiczny przydział pamięci w językach C i C++
Zarządzanie pamięcią przydzielaną dynamicznie w języku C
W języku C dynamiczny przydział pamięci realizowały funkcje:
void * malloc( size_t size ),
void * calloc( size_t nitems, size_t size ),
void * realloc( void * ptr, size_t size ).
Obszary pamięci przydzielone tymi funkcjami należało zwolnić funkcją:
void free( void * ptr ).
Zarządzanie pamięcią przydzielaną dynamicznie w języku C++
W języku C++ dynamiczny przydział pamięci realizować będzie:
operator new,
a zwalnianie przydzielonej pamięci:
operator delete.
Dynamiczny przydział pamięci w językach C i C++
Dynamiczny przydział pamięci w językach C i C++
Zarządzanie pamięcią przydzielaną dynamicznie w języku C
Funkcje przydzielające pamięć nie są częścią języka, pochodzą z biblioteki
zarządzania pamięcią.
Funkcje przedzielające pamięć traktowane są jako amorficzne bloki, o
liczonym w bajtach rozmiarze zdefiniowanym przez programistę.
Typ przdzielanych obszarów nie jest funkcją znany, programista musi
panować nad rozmiarami przydzielonych bloków.
Zarządzanie pamięcią przydzielaną dynamicznie w języku C++
Zarządzanie pamięcią zostało włączone języka, operatory new i delete są
znane kompilatorowi,
Stosowanie operatorów new i delete zapewnia aktywowanie konstruktorów
dla inicjalizacji tworzonych obieków oraz destruktorów dla czynności
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Przydział pamięci dla obiektu klasy Square
Square * p; p = new Square; cout << p->getSide(); delete p;
Użycie operatora new powoduje przydzielenie pamięci dla obiektu w obszarze zwanym stertą, oraz inicjalizację tego obiektu z wykorzystaniem odpowiedniego konstruktora.
Utworzenie obiektu i inicjalizacja — wywołanie konstruktora bezparametrowego
Usunięcie obiektu i czynności kończące — wywołanie destruktora
Square * a = new Square; // Aktywowanie konstruktora domy lnegoś Square * b = new Square( 10 ); // Aktywowanie konstruktora ogólnego
Square * c = new Square( *b ); // Aktywowanie konstruktora kopiuj cegoą
Można sterować rodzajem inicjalizacji
Usuwanie obiektu operatorem delete
delete a; delete b; delete c;
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Uwagi na temat zwalniania pamięci
Operator delete może być stosowany wyłącznie dla obiektów utworzonych operatorem new.
Mieszanie operatorów new i delete z funkcjami typu malloc i free daje niezdefiniowane rezultaty.
Jeżeli operator delete zostanie użyty dla wskaźnika zerowego, nic się nie stanie a operacja ta nie jest błędna.
Dwukrotne (lub wielokrotne) usunięcie tego samego obiektu jest błędem i prowadzi do problemów.
Uwaga — odwoływanie się do obszaru pamięci, który został wcześniej zwolniony jest błędem zarówno w C jak i w C++.
Square * p = new Square; . . .
delete p;
// To nie jest rozs dne: ą
p->setSide( 100 ); cout << p->getSide();
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Uwagi na temat zwalniania pamięci
Dobrą praktyką jest zerowanie wskaźnika tuż po zwolnieniu pamięci,
Pozwala to na kontrolowanie czy można odwołać się do obiektu wskazywanego w innym miejscu programu.
Uwaga — w jęzuku C++ można zakładać, że wskaźnik pusty (pokazujący na nic) ma wartość zero. Korzystanie ze stałej NULL wymaga włączenia przynajmniej pliku nagłówkowego stddef.h.
Square * p = new Square; . . . delete p; p = 0; // Wersja 1-sza: if( p ) { p->setSide( 100 ); . . . } // Wersja 2-ga: if( p != 0 ) { p->setSide( 100 ); . . . } // Wersja 3-cia: if( p != NULL ) { p->setSide( 100 ); . . . } To jest rozsądne:
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Przedział pamięci dla tablic obiektów
Operator new może być wykorzystany do utworzenia tablicy obiektów.
Stosowanie tego operatora zapewnia, że dla każdego elementu tablicy zostanie aktywowany konstruktor domyślny.
Square * sTab = new Square[ 10 ];
Tak utworzona tablica może być wykorzystana w normalny dla tablic sposób:
for( int i = 0; i < 10; i++ ) cout << sTab[ i ].getSide();
Taką tablicę należy jednak usunąć w specyficzny sposób:
delete [] sTab;
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Dynamiczne tablice mogą mieć rozmiar określony zmienną
int numOfSquares;
cout << "Podaj liczbe kwadratow :"; cin >> numOfSquares;
Square * sTab = new Square[ numOfSquares ]; for( int i = 0; i < numOfSquares; i++ )
cout << sTab[ i ].getSide() << endl; delete [] sTab;
Operatory new i delete mogą być stosowane dla typów wbudowanych
int n;
cout << "Podaj liczbe elementow:"; cin >> n;
int * iTab = new int[ n ]; for( int i = 0; i < n; i++ ) cout << iTab[ i ] << endl; delete [] iTab;
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Należy uważać na dynamicznie przydzielane obszary pamięci
int n;
cout << "Podaj liczbe elementow:"; cin >> n;
int * iTab = new int[ n ]; . . .
iTab = new int[ 10 ]; // Poprzednio przydzielony obszar został utracony . . .
for( int i = 0; i < n; i++ ) cout << iTab[ i ] << endl; delete [] iTab;
Można temu częściowo zaradzić
int * const iTab = new int[ n ];
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Przydział pamięci dla tablic wielowymiarowych
const int MAX_LINE_LEN = 256; int numOfLines;
char ( * lines )[ MAX_LINE_LEN ];
cout << "Podaj liczbe linii: "; cin >> numOfLines;
lines = new char[ numOfLines ][ MAX_LINE_LEN ];
for( int i = 0; i < numOfLines; i++ ) {
sprintf( lines[ i ], "Linia nr: %-2d", i + 1 ); cout << endl << lines[ i ];
}
delete [] lines;
Przy alokacji tablic wielowymiarowych wszystkie wymiary poza pierwszym muszą być nieujemnymi wyrażeniami o wartości znanej na etapie kompilacji. Wymiar pierwszy może być zadany zmienną.
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Gdy pamięci jest za mało...
void out_of_memory() {
cerr << "Brak pami ci";ę exit( EXIT_FAILURE ); }
Jeżeli operator new nie potrafi znaleźć ciągłego, wolnego obszaru pamięci dla obiektu: sprawdza czy programista zdefiniował specjalną funkcję obsługi takiej sytuacji, jeżeli taka funkcja istnieje, jest ona wywoływana,
w przeciwnym wypadku generowany jest wyjątek, dawniej rezultatem operatora była wartość zero.
Programista definiuje własną funkcję obsługi braku pamięci wykorzystując funkcję
set_new_handler()
zdefiniowaną w pliku nagłówkowym new.h.
Dynamiczny przydział pamięci w C++
Dynamiczny przydział pamięci w C++
Czy to działa ― sprawdź kiedy skończy się pamięć
#include <iostream> #include <cstdlib> #include <new> using namespace std; void out_of_memory() {
cerr << "Brak pami ci";ę exit( EXIT_FAILURE ); } int main() { set_new_handler( out_of_memory ); for(;;) new int[ 100000 ]; return EXIT_SUCCESS; }