C++/Zarządzanie pamięcią: Różnice pomiędzy wersjami

Z Wikibooks, biblioteki wolnych podręczników.
< C++
Usunięta treść Dodana treść
Nie podano opisu zmian
Lethern (dyskusja | edycje)
poprawki
Linia 3: Linia 3:
Rozważmy prosty przykład. Załóżmy, że chcemy stworzyć wektor 10 liczb typu całkowitego. Możemy to zrobić na dwa sposoby. W stylu znanym z języka C:
Rozważmy prosty przykład. Załóżmy, że chcemy stworzyć wektor 10 liczb typu całkowitego. Możemy to zrobić na dwa sposoby. W stylu znanym z języka C:
<source lang="cpp">
<source lang="cpp">
int *Wektor = (int*)malloc(sizeof(int)*10);
int *wektor = (int*) malloc (sizeof(int)*10);
free(Wektor);
free (wektor);
</source>
</source>


Albo w stylu C++:
Albo w stylu C++:
<source lang="cpp">
<source lang="cpp">
int *Wektor = new int[10];
int *wektor = new int[10];
delete [] Wektor;
delete [] wektor;
</source>
</source>


Od razu widać, że drugi zapis jest łatwiejszy i przyjemniejszy w użyciu. To jest podstawowa zaleta operatora new - krótszy zapis. Wystarczy wiedzieć jakiego typu ma być obiekt, który chcemy powołać do życia, nie martwiąc się o rozmiar alokowanego bloku pamięci. Za pomocą operatora new można również tworzyć tablice wielowymiarowe:

Od razu widać, że drugi zapis jest zdecydowanie łatwiejszy i przyjemniejszy w użyciu. To jest podstawowa zaleta operatora new - krótszy zapis. Wystarczy wiedzieć jakiego typu ma być obiekt, który chcemy powołać do życia, nie martwiąc się o rozmiar alokowanego bloku pamięci. Za pomocą operatora new można również tworzyć tablice wielowymiarowe:
<source lang="cpp">
<source lang="cpp">
int **Wektory = new int *[5];
int **wektory = new int *[5];
for( int i=0; i<5; ++i )
for (int i = 0; i < 5; ++i)
Wektory[i] = new int [10];
wektory[i] = new int [10];
</source>
</source>


W ten sposób stworzono tablicę dwuwymiarową którą statycznie zadeklarowalibyśmy jako:
W ten sposób stworzono tablicę dwuwymiarową którą statycznie zadeklarowalibyśmy jako:
<source lang="cpp">
<source lang="cpp">
int Wektory[5][10];
int wektory[5][10];
</source>
</source>


Jenak w przeciwieństwie do <code>int Wektory[5][10]</code>, która jest tablicą dwuwymiarową, nasze <code>int **Wektory</code> jest tablicą tablic i może być rozrzucone po całej pamięci.
Jenak w przeciwieństwie do <code>int wektory[5][10]</code>, która jest tablicą dwuwymiarową, nasze <code>int **wektory</code> jest tablicą tablic i może być rozrzucone po całej pamięci.



Ilość elementów poszczególnych wymiarów nie musi być jednakowa. Można np zadeklarować tablicę taką:
Ilość elementów poszczególnych wymiarów nie musi być jednakowa. Można np zadeklarować tablicę taką:
<source lang="cpp">
<source lang="cpp">
int **Wektory = new int *[2];
int **wektory = new int *[2];
Wektory[0] = new int [5];
wektory[0] = new int [5];
Wektory[1] = new int;
wektory[1] = new int;
</source>
</source>


Linia 40: Linia 38:
Deklaracja tablic o większej ilości wymiarów przebiega podobnie:
Deklaracja tablic o większej ilości wymiarów przebiega podobnie:
<source lang="cpp">
<source lang="cpp">
int ***Wektory = NULL; // deklarujemy tablicę 3-wymiarową
int ***wektory; // deklarujemy tablicę 3-wymiarową
Wektory = new int **[5]; // pierwszy wymiar
wektory = new int **[5]; // pierwszy wymiar
Wektory[0] = new int *[10]; // pierwszy element pierwszego wymiaru
wektory[0] = new int *[10]; // pierwszy element pierwszego wymiaru
Wektory[1] = new int *[3]; // drugi element pierwszego wymiaru
wektory[1] = new int *[3]; // drugi element pierwszego wymiaru
....
....
Wektory[0][1] = new int [3] // wymiar I = 0 -> wymiar II = 1 -> 3 elementy(tablica)
wektory[0][0] = new int [3] // wymiar I = 0 -> wymiar II = 1 -> 3 elementy(tablica)
Wektory[0][3] = new int [5] // wymiar I = 0 -> wymiar II = 3 -> 5 elementów(tablica)
wektory[0][1] = new int [5] // wymiar I = 0 -> wymiar II = 3 -> 5 elementów(tablica)
Wektory[1][2] = new int; // wymiar I = 1 -> wymiar II = 2 -> 1 element
wektory[1][0] = new int; // wymiar I = 1 -> wymiar II = 2 -> 1 element
...
...
</source>
</source>




Stosując ten algorytm ogólnie można deklarować tablice n-wymiarowe bez większego problemu.
Stosując ten sposób, ogólnie można deklarować tablice n-wymiarowe bez większego problemu.
Usuwanie tablic wielowymiarowych przebiega podobnie jak jednowymiarowych z tą różnicą, że usuwanie zaczynamy od "najgłębszego" wymiaru:
Usuwanie tablic wielowymiarowych przebiega podobnie jak jednowymiarowych, z tą różnicą, że usuwanie zaczynamy od "najgłębszego" wymiaru:
<source lang="cpp">
<source lang="cpp">
delete wektory[0][0]; // kasujemy pojedynczą zmienną
int ***Wektory = new int **[2];
Wektory[0] = new int *[2];
delete [] wektory[0][1];
Wektory[1] = new int *[2];
delete [] wektory[1][0];
Wektory[0][0] = new int; // pojedyncza zmienna dynamniczna! nie tablica
Wektory[0][1] = new int [5]; // zmienna tablicowa
Wektory[1][0] = new int [3];
Wektory[1][1] = new int [2];
...
// Kod programu
...
// III wymiar
delete Wektory[0][0]; // kasujemy pojedynczą zmienną
delete [] Wektory[0][1];
delete [] Wektory[1][0];
delete [] Wektory[1][1];
// II wymiar
// II wymiar
delete [] Wektory[0];
delete [] wektory[0];
delete [] Wektory[1];
delete [] wektory[1];
// I wymiar
// I wymiar
delete [] Wektory;
delete [] wektory;
</source>
</source>



Wersja z 06:28, 1 lis 2011

W języku C++ do alokowania pamięci służy operator new a do zwalniania - delete. W C++ można również stosować funkcje malloc i free, jednak należy być ostrożnym. Najczęstszym błędem jest mieszanie operatorów new i delete z funkcjami malloc i free, np. zwalnianie pamięci zaalokowanej przez new przy pomocy free.

Rozważmy prosty przykład. Załóżmy, że chcemy stworzyć wektor 10 liczb typu całkowitego. Możemy to zrobić na dwa sposoby. W stylu znanym z języka C:

int *wektor = (int*) malloc (sizeof(int)*10);
free (wektor);

Albo w stylu C++:

int *wektor = new int[10];
delete [] wektor;

Od razu widać, że drugi zapis jest łatwiejszy i przyjemniejszy w użyciu. To jest podstawowa zaleta operatora new - krótszy zapis. Wystarczy wiedzieć jakiego typu ma być obiekt, który chcemy powołać do życia, nie martwiąc się o rozmiar alokowanego bloku pamięci. Za pomocą operatora new można również tworzyć tablice wielowymiarowe:

int **wektory = new int *[5];
for (int i = 0; i < 5; ++i)
   wektory[i] = new int [10];

W ten sposób stworzono tablicę dwuwymiarową którą statycznie zadeklarowalibyśmy jako:

int wektory[5][10];

Jenak w przeciwieństwie do int wektory[5][10], która jest tablicą dwuwymiarową, nasze int **wektory jest tablicą tablic i może być rozrzucone po całej pamięci.

Ilość elementów poszczególnych wymiarów nie musi być jednakowa. Można np zadeklarować tablicę taką:

int **wektory = new int *[2];
wektory[0] = new int [5];
wektory[1] = new int;


Przy takiej deklaracji pierwszy wiersz ma 5 elementów (tablica) a drugi to jeden element. Deklaracja tablic o większej ilości wymiarów przebiega podobnie:

int ***wektory;             // deklarujemy tablicę 3-wymiarową
wektory = new int **[5];    // pierwszy wymiar
wektory[0] = new int *[10]; // pierwszy element pierwszego wymiaru
wektory[1] = new int *[3];  // drugi element pierwszego wymiaru
....
wektory[0][0] = new int [3] // wymiar I = 0 -> wymiar II = 1 -> 3 elementy(tablica)
wektory[0][1] = new int [5] // wymiar I = 0 -> wymiar II = 3 -> 5 elementów(tablica)
wektory[1][0] = new int;    // wymiar I = 1 -> wymiar II = 2 -> 1 element
...


Stosując ten sposób, ogólnie można deklarować tablice n-wymiarowe bez większego problemu. Usuwanie tablic wielowymiarowych przebiega podobnie jak jednowymiarowych, z tą różnicą, że usuwanie zaczynamy od "najgłębszego" wymiaru:

delete  wektory[0][0];        // kasujemy pojedynczą zmienną
delete [] wektory[0][1];
delete [] wektory[1][0];
// II wymiar
delete [] wektory[0];
delete [] wektory[1];
// I wymiar
delete [] wektory;

Zwrócić uwagę trzeba na dwie rzeczy:

  • delete [] używamy dla zmiennych tablicowych, a delete dla pojedynczych zmiennych
  • Kolejność zwalniania wymiarów jest odwrotna niż ich tworzenia


Drugą zaletą jest fakt, że przy okazji alokacji pamięci możemy wywołać odpowiedni konstruktor inicjując wartości zmiennych obiektu, np.

Test *test = new Test(1,2);

zakładając, że obiekt Test posiada dwie zmienne typu całkowitego i zdefiniowany konstruktor Test(int,int).

Kolejną korzyścią jest możliwość przeciążania. Jednak to już jest temat na inny rozdział.