Alocare Dinamică, C++ style

malloc și free își fac treaba foarte bine, dar odată cu apariția claselor a apărut nevoia pentru un mod de a aloca dinamic memorie prin care se apelează constructorul.

new

new T alocă memorie pentru un obiect de tipul T, apelează constructorul și întoarce un pointer către acesta. Se pot specifica și parametri pentru constructor.

Pentru a aloca un tip de date de bază:

uint32_t *ptr = new uint32_t;

Pentru a aloca un obiect cu alt constructor decât cel implicit:

Vector2 *vec = new Vector2(1.f, 3.f);

unde Vector2 are definiția:

class Vector2 {
public:
    float x, y;
    
    Vector2() {
        x = 0.f;
        y = 0.f;
    }
    
    Vector2(float x, float y) {
        this->x = x;
        this->y = y;
    }
};
delete

delete ptr apelează destructorul și eliberează memoria alocată pentru obiectul aflat la adresa ținută în ptr.

new[]

new T[len] alocă memorie contiguă pentru len obiecte de tipul T și apelează constructorul implicit pentru fiecare în parte, dacă T este clasă.

Pentru a aloca un array de len elemente cu tip de date de bază:

uint32_t *u = new uint32_t[len]; // Neinițializate
uint32_t *v = new uint32_t[len](); // Inițializate cu 0

Pentru a aloca un array de len obiecte de tip T:

T *v = new T[len]; // Constructorul cu 0 parametri
delete[]

delete[] v apelează destructorul pe fiecare element din arrayul care începe la adresa v și eliberează memoria alocată.

Atenție: delete v în loc de delete[] v șterge doar primul element dacă este apelat pe un array. Pointerul la array este în același timp pointerul la primul element.

Exerciții

  1. Creați o clasă ListNode care conține un număr natural info și 2 pointeri către alte ListNodeuri inițialize pe nullptr.
  2. Adăugați un constructor care ia ca argument un singur uint32_t info.
  3. Adăugați o metodă ListNode *insertAfter(const uint32_t &info) care inserează un nou nod după cel curent, păstrează integritatea listei dublu înlănțuite și întoarce adresa noului nod.
  4. Adăugați o metodă void printList() care afișează lista dublu înlănțuită începând cu nodul curent.
  5. Adăugați o metodă ListNode *erase() care elimină din lista dublu înlănțuită nodul curent, păstrează integritatea listei și întoarce adresa următorului nod din listă.
  6. Adăugați o metodă și variabile membru utile astfel încât fiecare nod din ListNode să rețină și divizorii primi ai numărului info, fără a consuma inutil memorie și fără a crea memory leakuri.
  7. Creați o clasă TreeNode care conține un număr natural info și un array alocat dinamic de pointeri către fiii nodului.
  8. Adăugați un constructor care ia ca argument un singur uint32_t info.
  9. Adăugați un constructor care ia ca argument un uint32_t info și un TreeNode *father care creează un nod nou și îl adaugă ca fiu al nodului father, dacă acesta este diferit de nullptr.
  10. Adăugați o metodă TreeNode *addChild(TreeNode *child) care adaugă un nod deja existent ca fiu al nodului curent.
  11. Adăugați o metodă TreeNode *createChild(const uint32_t &info) care creează un nod nou cu informația info și o adaugă ca fiu al nodului curent.
  12. Testați și asigurați-vă că nu aveți memory leakuri.