Ce se întoarce nou? Obiecte dinamice cu numărătoare de referințe. Obiecte dinamice cu management standard de memorie

15.8. Operatori noi și ștergeți

În mod implicit, alocarea unui obiect de clasă dintr-un heap și eliberarea memoriei pe care a ocupat-o se face folosind operatori globali new() și delete() definite în bibliotecă standard C++. (Am discutat despre acești operatori în Secțiunea 8.4.) Dar o clasă își poate implementa propria strategie de gestionare a memoriei furnizând operatori membri cu același nume. Dacă sunt definiți într-o clasă, ei sunt apelați în loc de operatori globali pentru a aloca și elibera memorie pentru obiectele acestei clase.

Să definim operatorii new() și delete() din clasa noastră Screen.

Operatorul membru new() trebuie să returneze o valoare de tip void* și să ia ca prim parametru o valoare de tip size_t, unde size_t este tipul definit în fișierul antet al sistemului. Iată anunțul lui:

void *operator new(size_t);

Când new() este folosit pentru a crea un obiect de tip clasă, compilatorul verifică dacă un astfel de operator este definit în acea clasă. Dacă da, atunci el este cel care este chemat să aloce memorie pentru obiect, în altfel– operator global new(). De exemplu, următoarea instrucțiune

Ecran *ps = ecran nou;

creează un obiect Screen în heap și, deoarece această clasă are un operator new(), este apelată. Parametrul size_t al operatorului este inițializat automat la o valoare egală cu dimensiunea ecranului în octeți.

Adăugarea sau eliminarea new() la o clasă nu are niciun efect asupra codului utilizatorului. Apelul către nou arată la fel atât pentru operatorul global, cât și pentru operatorul membru. Dacă clasa Screen nu avea propriul new(), atunci apelul ar rămâne corect, doar operatorul global ar fi apelat în loc de operatorul membru.

Folosind operatorul de rezoluție globală, puteți apela global new() chiar dacă clasa Screen își definește propria versiune:

Ecran *ps = ::Ecran nou;

void operator delete(void *);

Când operandul de ștergere este un pointer către un obiect de tip de clasă, compilatorul verifică dacă operatorul delete() este definit în acea clasă. Dacă da, atunci este chemat pentru a elibera memoria, în caz contrar - versiune globală operator. Următoarele instrucțiuni

Eliberează memoria ocupată de obiectul Screen indicat de ps. Deoarece Screen are un operator membru delete(), acesta este ceea ce este folosit. Parametrul operator de tip void* este inițializat automat la valoarea ps. Adăugarea delete() la sau eliminarea acesteia dintr-o clasă nu are niciun efect asupra codului utilizatorului. Apelul de ștergere arată la fel atât pentru operatorul global, cât și pentru operatorul membru. Dacă clasa Screen nu avea propriul operator delete(), atunci apelul va rămâne corect, doar operatorul global ar fi apelat în locul operatorului membru.

Folosind operatorul de rezolvare a domeniului global, puteți apela global delete() chiar dacă Screen are propria sa versiune definită:

În general, operatorul delete() utilizat trebuie să se potrivească cu operatorul new() cu care a fost alocată memoria. De exemplu, dacă ps indică o zonă de memorie alocată de global new(), atunci global delete() ar trebui să fie folosit pentru a o elibera.

Operatorul delete() definit pentru un tip de clasă poate lua doi parametri în loc de unul. Primul parametru trebuie să fie în continuare de tip void*, iar al doilea trebuie să fie de tipul predefinit size_t (nu uitați să includeți fișierul antet):

// înlocuiește

// void operator delete(void *);

Dacă al doilea parametru este prezent, compilatorul îl inițializează automat cu o valoare egală cu dimensiunea în octeți a obiectului adresat de primul parametru. (Această opțiune este importantă într-o ierarhie de clasă, unde operatorul delete() poate fi moștenit de o clasă derivată. Moștenirea este discutată mai detaliat în Capitolul 17.)

Să ne uităm mai detaliat la implementarea operatorilor new() și delete() în clasa Screen. Strategia noastră de alocare a memoriei se va baza pe lista legată Obiecte ecran, al căror început este indicat de membrul freeStore. De fiecare dată când este apelat operatorul membru new(), următorul obiect din listă este returnat. Când se apelează delete(), obiectul este returnat în listă. Dacă, la crearea unui obiect nou, lista adresată freeStore este goală, atunci operatorul global new() este apelat pentru a obține un bloc de memorie suficient pentru a stoca obiecte screenChunk din clasa Screen.

Atât screenChunk, cât și freeStore sunt de interes doar pentru Screen, așa că îi vom face membri privați. În plus, pentru toate obiectele create din clasa noastră, valorile acestor membri trebuie să fie aceleași și, prin urmare, trebuie să fie declarate statice. Pentru a susține structura de listă legată a obiectelor Screen, avem nevoie de un al treilea membru următor:

void *operator new(size_t);

void operator delete(void *, size_t);

Ecran static *freeStore;

static const int screenChunk;

Iată o posibilă implementare a operatorului new() pentru clasa Screen:

#include „Screen.h”

#include cstddef

// membrii statici sunt inițializați

// V fișierele sursă programe, nu în fișierele antet

Ecran *Ecran::freeStore = 0;

const int Screen::screenChunk = 24;

void *Screen::operator new(size_t size)

dacă (!freeStore) (

// lista legată este goală: get bloc nou

// este apelat operatorul global new

size_t chunk = screenChunk * dimensiune;

reinterpret_cast Screen* (car[ bucată nouă]);

// include blocul primit în listă

p != &freeStore[ screenChunk - 1 ];

freeStore = freeStore-next;

Și iată implementarea operatorului delete():

void Screen::operator delete(void *p, size_t)

// inserați obiectul „eliminat” înapoi,

// la lista liberă

(static_cast Screen* (p))-next = freeStore;

freeStore = static_cast Screen* (p);

Operatorul new() poate fi declarat într-o clasă fără un delete() corespunzător. În acest caz, obiectele sunt eliberate folosind operatorul global cu același nume. De asemenea, este permisă declararea operatorului delete() fără new(): obiectele vor fi create folosind operatorul global cu același nume. Cu toate acestea, de obicei acești operatori sunt implementați simultan, ca în exemplul de mai sus, deoarece dezvoltatorul clasei are de obicei nevoie de ambele.

Sunt membri statici ai clasei, chiar dacă programatorul nu îi declară în mod explicit ca atare și sunt supuși restricțiilor obișnuite pentru astfel de funcții membre: nu li se trece un pointer this și, prin urmare, pot accesa doar membrii statici direct. (Consultați discuția despre funcțiile membre statice din Secțiunea 13.5.) Motivul pentru care acești operatori devin statici este că sunt apelați fie înainte ca obiectul clasei să fie construit (new()), fie după ce acesta este distrus (delete()).

Alocarea memoriei folosind operatorul new(), de exemplu:

Ecran *ptr = ecran nou (10, 20);

// Pseudocod în C++

ptr = Screen::operator new(sizeof(Screen));

Ecran::Ecran(ptr, 10, 20);

Cu alte cuvinte, operatorul new() al clasei este mai întâi apelat pentru a aloca memorie pentru obiect, iar apoi obiectul este inițializat de către constructor. Dacă new() eșuează, o excepție de tip bad_alloc este ridicată și constructorul nu este apelat.

Eliberarea memoriei folosind operatorul delete(), de exemplu:

este echivalent cu executarea secvenţială a următoarelor instrucţiuni:

// Pseudocod în C++

Ecran::~Ecran(ptr);

Screen::operator delete(ptr, sizeof(*ptr));

Astfel, atunci când un obiect este distrus, mai întâi este apelat destructorul de clasă, iar apoi operatorul delete() definit în clasă este apelat pentru a elibera memoria. Dacă ptr este 0, atunci nici destructorul, nici delete() nu sunt apelate.

15.8.1. Operatori noi și ștergeți

Operatorul new(), definit în subsecțiunea anterioară, este apelat numai atunci când memorie este alocată unui singur obiect. Deci, în această instrucțiune new() din clasa Screen este numită:

Ecran *ps = ecran nou (24, 80);

în timp ce mai jos, operatorul global new() este apelat pentru a aloca memorie din heap pentru o matrice de obiecte de tip Screen:

// numit Screen::operator new()

Ecran *psa = ecran nou;

De asemenea, clasa poate declara operatori new() și delete() pentru lucrul cu matrice.

Operatorul membru new() trebuie să returneze o valoare de tip void* și să ia o valoare de tip size_t ca prim parametru. Iată anunțul lui pentru Screen:

void *operator new(size_t);

Când se utilizează new pentru a crea o matrice de obiecte de tipul clasei, compilatorul verifică dacă clasa are un operator new() definit. Dacă da, atunci este apelat pentru a aloca memorie pentru matrice; în caz contrar, global new() este apelat. ÎN urmând instrucțiunile o matrice de zece obiecte Screen este creată în heap:

Ecran *ps = ecran nou;

Această clasă are operatorul new(), motiv pentru care este apelată pentru a aloca memorie. Parametrul său size_t este inițializat automat la o valoare egală cu cantitatea de memorie, în octeți, necesară pentru a deține zece obiecte Screen.

Chiar dacă o clasă are un operator membru new(), programatorul poate apela global new() pentru a crea o matrice folosind operatorul de rezoluție globală:

Ecran *ps = ::Ecran nou;

Operatorul delete(), care este membru al clasei, trebuie să fie de tip void și să ia void* ca prim parametru. Iată cum arată anunțul lui ecran:

void operator delete(void *);

Pentru a șterge o matrice de obiecte de clasă, ștergerea trebuie să fie numită astfel:

Când operandul de ștergere este un pointer către un obiect de tip de clasă, compilatorul verifică dacă operatorul delete() este definit în acea clasă. Dacă da, atunci este apelată pentru a elibera memoria, în caz contrar, se numește versiunea sa globală. Un parametru de tip void* este inițializat automat la valoarea adresei începutului zonei de memorie în care se află matricea.

Chiar dacă o clasă are un operator membru delete(), programatorul poate apela global delete() folosind operatorul de rezoluție globală a domeniului:

Adăugarea sau eliminarea operatorilor new() sau delete() la o clasă nu afectează codul utilizatorului: apelurile atât către operatori globali, cât și către operatori membri arată la fel.

Când se creează o matrice, new() este mai întâi chemat pentru a aloca memoria necesară, iar apoi fiecare element este inițializat folosind constructorul implicit. Dacă o clasă are cel puțin un constructor, dar niciun constructor implicit, atunci apelarea new() este considerată o eroare. Nu există o sintaxă pentru specificarea inițializatorilor de elemente ale matricei sau a argumentelor constructorului de clasă atunci când se creează o matrice în acest mod.

Când o matrice este distrusă, destructorul de clasă este mai întâi apelat pentru a distruge elementele, iar apoi operatorul delete() este apelat pentru a elibera toată memoria. Este important să folosiți sintaxa corectă. Dacă instrucțiunile

ps indică o serie de obiecte de clasă, apoi absența paranteze pătrate va face ca destructorul să fie apelat doar pentru primul element, deși memoria va fi eliberată complet.

Operatorul membru delete() poate avea doi parametri mai degrabă decât unul, iar al doilea trebuie să fie de tip size_t:

// înlocuiește

// void operator delete(void*);

void operator delete(void*, size_t);

Dacă al doilea parametru este prezent, compilatorul îl inițializează automat cu o valoare egală cu cantitatea de memorie alocată pentru matrice în octeți.

Din carte Ghid de ajutorîn C++ autor Stroustrap Bjarne

R.5.3.4 Operația de ștergere Operația de ștergere distruge un obiect creat folosind new.deallocation-expression: ::opt delete cast-expression::opt delete cast-expression Rezultatul este de tip void. Operandul de ștergere trebuie să fie un pointer, care returnează nou. Efectul utilizării operației de ștergere

Din carte Microsoft Visual C++ și MFC. Programare pentru Windows 95 și Windows NT autor Frolov Alexandru Viaceslavovici

Operatorii noi și de ștergere Operatorul nou creează un obiect de tipul specificat. Procedând astfel, alocă memoria necesară pentru a stoca obiectul și returnează un pointer care indică spre el. Dacă din anumite motive memoria nu poate fi obținută, operatorul revine valoare nulă. Operator

Din carte Utilizare eficientă C++. 55 modurile corecteîmbunătățiți structura și codul programelor dvs de Meyers Scott

Regula 16: Folosește același lucru forme noiși ștergeți Ce este în neregulă cu următorul fragment?std::string *stringArray = new std::string;...delete stringArray;La prima vedere, totul este în in perfecta ordinefolosind noi Aplicația corespunzătoare este ștearsă, dar ceva aici este complet greșit. Comportamentul programului

Din cartea Windows Script Host pentru Windows 2000/XP autor Popov Andrei Vladimirovici

Capitolul 8 Configurarea noilor și a ștergerii În zilele noastre, când mediile de calcul au suport încorporat pentru colectarea gunoiului (cum ar fi Java și .NET), abordarea manuală a C++ pentru gestionarea memoriei poate părea puțin depășită. Cu toate acestea, mulți dezvoltatori care creează exigenți

Din cartea Standarde de programare în C++. 101 reguli și recomandări autor Alexandrescu Andrei

Metoda Delete Dacă parametrul de forță este fals sau nu este specificat, atunci folosind metoda Delete va fi imposibil să ștergeți un director cu un atribut de doar citire. Setarea forței la true va permite ca astfel de directoare să fie șterse imediat Când utilizați metoda Delete, nu contează dacă este specificat

Din cartea Flash Reference autor Echipa de autori

Metoda Delete Dacă parametrul de forță este fals sau nu este specificat, atunci folosind metoda Delete va fi imposibil să ștergeți un fișier cu un atribut numai pentru citire. Setarea forței la true va permite ca astfel de fișiere să fie șterse imediat. Notă Puteți utiliza metoda DeleteFile în loc de metoda Delete.

Din cartea Firebird DATABASE DEVELOPER'S GHIDE de Borri Helen

Operatori relaționali și logici Operatorii relaționali sunt utilizați pentru a compara valorile a două variabile. Acești operatori, descriși în tabel. P2.11, poate returna numai valori logice adevărate sau false. Tabelul P2.11. Operatori relaționali Operator Condiție, când

Din cartea Linux și UNIX: programare shell. Ghidul dezvoltatorului. de Tainsley David

45. new și delete ar trebui să fie întotdeauna proiectate împreună. Rezumat Fiecare supraîncărcare a operatorului void* new(parms) dintr-o clasă trebuie să fie însoțită de o supraîncărcare corespunzătoare a operatorului void delete(void* , parms), unde parms este o listă de tipuri parametri suplimentari(dintre care primul este întotdeauna std::size_t). Aceleaşi

Din cartea Ajutor SQL a autorului

delete - Ștergerea unui obiect, element de matrice sau variabilă delete (Operator) Acest operator este utilizat pentru a șterge un obiect, o proprietate de obiect, un element de matrice sau variabile dintr-un script variabilă, nume

Din cartea Înțelegerea SQL-ului de Gruber Martin

Declarația DELETE DELETE cerere folosit pentru a șterge rânduri întregi de tabel. SQL nu permite unei singure instrucțiuni DELETE să șteargă rânduri din mai mult de un tabel. O solicitare DELETE care modifică doar un rând curent al cursorului se numește ștergere poziționată.

Din cartea autorului

15.8. Operatorii new și delete În mod implicit, alocarea unui obiect de clasă dintr-un heap și eliberarea memoriei ocupate de acesta se realizează folosind operatorii globali new() și delete() definiți în biblioteca standard C++. (Ne-am uitat la acești operatori în Secțiunea 8.4.) Dar o clasă poate implementa

Din cartea autorului

15.8.1. Operatorii new și delete Operatorul new(), definit în subsecțiunea anterioară, este apelat numai atunci când este alocată memorie pentru un singur obiect. Deci, în această instrucțiune, new() din clasa Screen este numită: Screen::operator new()Screen *ps = new Screen(24, 80), în timp ce se apelează mai jos

Matricele și pointerii sunt de fapt strâns legate. Numele matricei este indicator constant, a cărei valoare este adresa primului element al matricei (&arr). Prin urmare, numele matricei poate fi un inițializator de pointer căruia se vor aplica toate regulile de aritmetică a adresei asociate cu pointerii. Exemplu de program:
Programul 11.1

#include folosind namespace std; int main() ( const int k = 10; int arr[k]; int *p = arr; // pointerul indică primul element al tabloului pentru (int i = 0; i< 10; i++){ *p = i; p++; // указатель указывает на elementul următor) p = arr; // returnează un pointer la primul element pentru (int i = 0; i< 10; i++){ cout << *p++ << " "; } cout << endl; // аналогично: for (int i = 0; i < 10; i++){ cout << *(arr + i) << " "; } cout << endl; p = arr; // выводим адреса элементов: for (int i = 0; i < 10; i++){ cout << "arr[" << i << "] => " << p++ << endl; } return 0; }

Ieșire program:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 arr => 0xbffc8f00 arr => 0xbffc8f04 arr => 0xbffc8f08 arr => 0xbffc8f0c arr => 1xbffc8f0c arr => 1xbffc8f0c => 8xb arr => 8xb 0xbffc8f18 arr = > 0xbffc8f1c arr => 0xbffc8f20 arr => 0xbffc8f24

Expresia arr[i] – accesarea unui element prin index corespunde expresiei *(arr + i) , care se numește offset indicator(linia 22). Această expresie ilustrează mai clar modul în care C++ funcționează de fapt cu elemente de matrice. Variabila contor i indică câte elemente trebuie să fie compensate față de primul element. Linia 17 afișează valoarea elementului de matrice după dereferențierea indicatorului.

Ce înseamnă expresia *p++? Operatorul * are o prioritate mai mică, în timp ce incrementul postfix este asociativ de la stânga la dreapta. Prin urmare, în această expresie complexă, adresarea indirectă (accesarea valorii unui element de matrice) va fi efectuată mai întâi, iar apoi indicatorul va fi incrementat. În caz contrar, această expresie ar putea fi reprezentată ca: cout Nota. Operatorul sizeof() aplicat unui nume de matrice va returna dimensiunea întregii matrice (nu primul element).
Nota. Operatorul de adresă (&) este folosit pentru elementele matrice în același mod ca și pentru variabilele obișnuite (elementele matrice sunt uneori numite variabile indexate). De exemplu, &arr . Prin urmare, puteți obține întotdeauna un pointer către orice element al matricei. Totuși, operația &arr (unde arr este numele matricei) va returna adresa întregii matrice și astfel, de exemplu, o operație (&arr + 1) va însemna un pas de dimensiunea unui tablou, adică obținerea unui pointer către element de lângă ultimul.

Avantajele utilizării pointerilor atunci când lucrați cu elemente de matrice

Să ne uităm la două exemple de programe care duc la același rezultat: valori noi de la 0 la 1999999 sunt atribuite elementelor matricei și sunt scoase la evidență.
Programul 11.2

#include folosind namespace std; int main() (const int n = 2000000; int masa[n] (); for (int i = 0; i< n; i++) { mass[i] = i; cout << mass[i]; } return 0; }

Programul 11.3

#include folosind namespace std; int main() (const int n = 2000000; int masa[n] (); int *p = masa; for (int i = 0; i< n; i++) { *p = i; cout << *p++; } return 0; }

Programul 11.3 va rula mai repede decât programul 11.2 (pe măsură ce numărul de elemente crește, programul 11.3 va deveni mai eficient)! Motivul este că Programul 11.2 recalculează locația (adresa) elementului de matrice curent în raport cu primul de fiecare dată (11.2, liniile 12 și 13). În Programul 11.3, adresa primului element este accesată o dată când indicatorul este inițializat (11.3, linia 11).

Matrice în afara limitelor

Să notăm un alt aspect important al lucrului cu matrice C în C++. Nu este disponibil în C++ monitorizarea conformității cu limitele matricei C. Că. Responsabilitatea pentru observarea modului de procesare a elementelor în limitele matricei revine în întregime dezvoltatorului algoritmului. Să ne uităm la un exemplu.
Programul 11.4

#include #include #include folosind namespace std; int main() ( int mas; default_random_engine rnd(time(0)); uniform_int_distribution < 10; i++) mas[i] = d(rnd); cout << "Элементы массива:" << endl; for (int i = 0; i < 10; i++) cout << mas[i] << endl; return 0; }

Programul va scoate ceva de genul acesta:

Elemente matrice: 21 58 38 91 23 5 38 -1219324996 -1074960992 0

A apărut o eroare intenționată în programul 11.4. Dar compilatorul nu va raporta o eroare: tabloul are cinci elemente declarate, dar buclele presupun că există 10 elemente! Ca rezultat, doar cinci elemente vor fi inițializate corect (este posibilă o corupție suplimentară a datelor) și vor fi scoase împreună cu „gunoiul”. C++ oferă capacitatea de a controla limitele utilizând funcțiile de bibliotecă begin() și end() (trebuie să includeți fișierul antet iterator). Modificarea programului 11.4
Programul 11.5

#include #include #include #include folosind namespace std; int main() ( int mas; int *first = begin(mas); int *last = end(mas); default_random_engine rnd(time(0)); uniform_int_distribution d(10, 99);<< "Элементы массива:" << endl; while(first != last) { cout << *first++ << " "; } return 0; }

while(first != last) ( *first = d(rnd); first++; ) first = begin(mas);
cout

Funcțiile begin() și end() returnează . Vom acoperi conceptul de iteratoare mai târziu, dar deocamdată vom spune că aceștia se comportă ca pointeri care indică primul element (primul) și elementul care urmează ultimul (ultimul). În programul 11.5, pentru compactitate și comoditate, am înlocuit bucla for cu o buclă while (din moment ce nu mai avem nevoie de un contor aici - folosim aritmetica pointerului). Având doi pointeri, putem formula cu ușurință o condiție pentru ieșirea din buclă, deoarece la fiecare pas al buclei primul pointer este incrementat.

O altă modalitate de a face traversarea elementelor matricei mai sigură este să utilizați bucla bazată pe intervale pe care am menționat-o în subiectul ()
Operațiuni noi și șterge

Înainte de a vă familiariza cu indicatorii, știați singura modalitate de a scrie date mutabile în memorie prin variabile. O variabilă este o zonă numită a memoriei. Blocurile de memorie pentru variabilele corespunzătoare sunt alocate la pornirea programului și sunt utilizate până când acesta se termină. Folosind pointeri, puteți crea blocuri de memorie fără nume de un anumit tip și dimensiune (și, de asemenea, le puteți elibera) în timp ce programul în sine rulează. Acest lucru dezvăluie o caracteristică remarcabilă a pointerilor, care este dezvăluită cel mai pe deplin în programarea orientată pe obiecte atunci când se creează clase.

Alocarea dinamică a memoriei se face folosind noua operație. Sintaxă:

data_type *pointer_name = new data_type;

De exemplu:
Int *a = int nou; // Declararea unui pointer de tip int int *b = new int(5); // Inițializați indicatorul

#include Partea dreaptă a expresiei spune că new cere un bloc de memorie pentru a stoca date de tip int . Dacă memoria este găsită, adresa este returnată și atribuită unei variabile pointer de tip int. Acum puteți accesa doar memoria creată dinamic folosind pointeri! Un exemplu de lucru cu memoria dinamică este prezentat în Programul 3.<< *c << endl; delete a; delete b; delete c; return 0; }

După lucrul cu memoria alocată, aceasta trebuie eliberată (returnată, pusă la dispoziție pentru alte date) folosind operația de ștergere. Controlul consumului de memorie este un aspect important al dezvoltării aplicațiilor. Erorile în care memoria nu este eliberată au ca rezultat „ scurgeri de memorie", care la rândul său poate provoca blocarea programului. Operația de ștergere poate fi aplicată unui pointer nul (nullptr) sau unuia creat cu new (adică new și delete sunt folosite în perechi).

Matrice dinamice

Matrice dinamică este un tablou a cărui dimensiune este determinată în timpul execuției programului. Strict vorbind, o matrice C nu este dinamică în C++. Adică, puteți determina doar dimensiunea matricei, iar modificarea dimensiunii matricei în timp ce programul rulează este încă imposibilă. Pentru a obține o matrice de dimensiunea necesară, trebuie să alocați memorie pentru o nouă matrice și să copiați datele din cea originală în ea, apoi să eliberați memoria alocată anterior pentru matricea originală. Adevăratul tip de matrice dinamică în C++ este , pe care îl vom analiza mai târziu. Pentru a aloca memorie pentru o matrice, se folosește noua operație. Sintaxa pentru alocarea memoriei pentru o matrice este:
pointer = tip nou [dimensiune] . De exemplu:

Int n = 10; int *arr = nou int[n];

Memoria este eliberată folosind operatorul de ștergere:

Șterge arr;

În acest caz, dimensiunea matricei nu este specificată.
Exemplu de program. Umpleți tabloul de întregi dinamici arr1 cu numere aleatorii. Afișați matricea sursă. Rescrie toate elementele cu numere de succesiune impare (1, 3, ...) într-o nouă matrice dinamică de întregi arr2. Tipăriți conținutul matricei arr2.
Programul 11.7

#include #include #include folosind namespace std; int main() ( int n; cout<< "n = "; cin >>n; int *arr1 = nou int[n];< n; i++) { arr1[i] = d(rnd); cout << arr1[i] << " "; } cout << endl; int *arr2 = new int; for (int i = 0; i < n / 2; i++) { arr2[i] = arr1; cout << arr2[i] << " "; } delete arr1; delete arr2; return 0; } n = 10 73 94 17 52 11 76 22 70 57 68 94 52 76 70 68

default_random_engine rnd(time(0)); uniform_int_distribution d(10, 99);

pentru (int i = 0; i

Știm că în C++, o matrice bidimensională este o matrice de matrice. Prin urmare, pentru a crea o matrice dinamică bidimensională, este necesar să se aloce memorie într-o buclă pentru fiecare matrice de intrare, după ce s-a determinat în prealabil numărul de matrice de creat. În acest scop este folosit
pointer la pointer
, cu alte cuvinte, o descriere a unui tablou de pointeri:

#include #include #include #include Int **arr = new int *[m]; unde m este numărul de astfel de tablouri (rânduri ale unui tablou bidimensional).<< "Введите количество строк:" << endl; cout << "m = "; cin >> m;<< "введите количество столбцов:" << endl; cout << "n = "; cin >cout< m; i++) { arr[i] = new int[n]; for (int j = 0; j < n; j++) { arr[i][j] = d(rnd); } } // вывод массива: for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { cout << arr[i][j] << setw(3); } cout << "\n"; } // освобождение памяти выделенной для каждой // строки: for (int i = 0; i < m; i++) delete arr[i]; // освобождение памяти выделенной под массив: delete arr; return 0; } Введите количество строк: m = 5 введите количество столбцов: n = 10 66 99 17 47 90 70 74 37 97 39 28 67 60 15 76 64 42 65 87 75 17 38 40 81 66 36 15 67 82 48 73 10 47 42 47 90 64 22 79 61 13 98 28 25 13 94 41 98 21 28

>n;
  1. int **arr = new int *[m];
  2. // umplerea matricei: for (int i = 0; i
  3. Întrebări
  4. Care este relația dintre pointeri și matrice?
  5. De ce este mai eficientă folosirea pointerilor la iterarea elementelor matricei decât utilizarea operației de index?
  6. Care este esența conceptului de „scurgere de memorie”?
Enumerați modalități de a împiedica limitele matricei să depășească limitele?
Ce este o matrice dinamică? De ce o matrice C nu este în mod inerent dinamică în C++?

Descrieți procesul de creare a unei matrice bidimensionale dinamice

Prezentare pentru lecție

Teme pentru acasă

Folosind matrice dinamice, rezolvați următoarea problemă: Având în vedere un tablou întreg A de dimensiunea N. Rescrie toate numerele pare din matricea originală (în aceeași ordine) într-o nouă matrice întregă B și imprimă dimensiunea matricei rezultate B și conținutul său.
  1. Manual
  2. §62 (10) §40 (11)
  3. Literatură
  4. Lafore R. Programare orientată pe obiecte în C++ (ed. a IV-a). Petru: 2004
  5. Prata, Stefan. limbaj de programare C++. Prelegeri și exerciții, ed. a VI-a: Trad. din engleză - M.: SRL „I.D. William”, 2012

Lippman B. Stanley, Josie Lajoie, Barbara E. Mu. limbaj de programare C++. Curs de bază. Ed. al 5-lea. M: SRL „I.D. Williams”, 2014

Elline A. C++. De la lamer la programator. Sankt Petersburg: Peter, 2015

Shildt G. C++: Curs de bază, ed. a III-a. M.: Williams, 2010

După cum știți, în limbajul C, funcțiile malloc() și free() sunt folosite pentru a aloca și elibera dinamic memoria. Cu toate acestea, C++ conține doi operatori care efectuează alocarea și dealocarea memoriei mai eficient și mai simplu. Acești operatori sunt noi și ștergi. Forma lor generală este:

variabilă pointer = tip_variabilă nou;

Operatorul de ștergere ar trebui utilizat numai pentru pointerii către memorie alocați folosind operatorul nou. Utilizarea operatorului de ștergere cu alte tipuri de adrese poate cauza probleme serioase.

Există o serie de avantaje în utilizarea new față de utilizarea malloc(). În primul rând, noul operator calculează automat dimensiunea memoriei necesare. Nu este nevoie să folosiți operatorul sizeof(). Mai important, vă împiedică să alocați accidental o cantitate greșită de memorie. În al doilea rând, noul operator returnează automat un indicator la tipul necesar, deci nu este nevoie să utilizați un operator de conversie de tip. În al treilea rând, așa cum va fi descris în scurt timp, este posibilă inițializarea unui obiect folosind noul operator. În cele din urmă, este posibil să supraîncărcați operatorul nou și operatorul de ștergere global sau în raport cu clasa care este creată.

Mai jos este un exemplu simplu de utilizare a operatorilor new și delete. Rețineți utilizarea unui bloc try/catch pentru a urmări erorile de alocare a memoriei.

#include
#include
int main()
{
int *p;
incearca (
p = int nou; // alocă memorie pentru int
) prinde (xalloc xa) (
cout<< "Allocation failure.\n";
întoarcere 1;
}
*p = 20; // atribuind acestei locații de memorie valoarea 20
cout<< *р; // демонстрация работы путем вывода значения
șterge p; // eliberând memoria
întoarce 0;
}

Acest program atribuie variabilei p adresa unui bloc de memorie suficient de mare pentru a conține un număr întreg. Apoi, acestei memorie i se atribuie o valoare și conținutul memoriei este afișat pe ecran. În cele din urmă, memoria alocată dinamic este eliberată.

După cum sa menționat, puteți inițializa memoria folosind noul operator. Pentru a face acest lucru, trebuie să specificați valoarea de inițializare în paranteze după numele tipului. De exemplu, în exemplul următor, memoria indicată de p este inițializată la 99:

#include
#include
int main()
{
int *p;
incearca (
p = new int(99); // inițializarea 99th
) prinde (xalloc xa) (
cout<< "Allocation failure.\n";
întoarcere 1;
}
cout<< *p;
șterge p;
întoarce 0;
}

Puteți folosi new pentru a aloca matrice. Forma generală pentru o matrice unidimensională este:

indicator_variabilă = tip_variabilă nou [dimensiune];

Aici dimensiunea determină numărul de elemente din matrice. Există o limitare importantă de reținut atunci când plasați o matrice: nu poate fi inițializată.

Pentru a elibera o matrice alocată dinamic, trebuie să utilizați următoarea formă a operatorului de ștergere:

Shildt G. C++: Curs de bază, ed. a III-a. M.: Williams, 2010

Aici parantezele informează operatorul de ștergere să elibereze memoria alocată pentru matrice.

Următorul program alocă memorie pentru o matrice de 10 elemente float. Elementelor matricei li se atribuie valori de la 100 la 109, iar apoi conținutul matricei este imprimat pe ecran:

#include
#include
int main()
{
plutire *p;
int i;
incearca (
p = flotor nou ; // obținerea celui de-al zecelea element al matricei
) catch(xalloc xa) (
cout<< "Allocation failure.\n";
întoarcere 1;
}
// atribuirea valorilor de la 100 la 109
pentru (i=0; i<10; i + +) p[i] = 100.00 + i;
// scoate conținutul matricei
pentru (i=0; i<10; i++) cout << p[i] << " ";
șterge p; // ștergerea întregii matrice
întoarce 0;
}

  • Serghei Savenkov

    un fel de recenzie „scurtă”... de parcă s-ar grăbi undeva