Dynamická pole (operátory new[] a delete[])

C++ Pokročilé konstrukce v C++ Dynamická pole (operátory new[] a delete[])

Aritmetika pointerů

Ještě před samotnou správou dynamických polí je potřeba vysvětlit některé početní operace s pointery. Je samozřejmé, že například operátory / nebo *(krát) nemají v souvislosti s pointery žádný smysl, násobit nebo dělit adresu v paměti je přeci hloupost. Nesmysl je i sčítání dvou pointerů, dostali bychom tím jen nějakou adresu v paměti a vůbec netušili co na ní je. Všechny početní operace si samozřejmě můžete vynutit explicitním přetypováním, ale jediné operace, které jsou povolené implicitně, jsou sčítání a odčítání pointeru s celým číslem a odečítání dvou pointerů, což je operace, která se používá v určitých specifických případech. Můžeme například provést takovouto operaci:

int* ptr;
...
*(ptr + 3) = 132;  //přiřazení hodnoty 132 na adresu, na kterou ukazuje ptr + 3

Pointery ovšem mají odlišná pravidla pro sčítání a odčítání, než základní datové typy. Kdybychom ptr přetypovali na int, výsledek operace ptr + 1 by byla hodnota adresy na kterou ukazuje ptr zvětšená o 1. Pokud ovšem ptr necháme jako int*, k adrese se nepřidá hodnota 1, ale taková hodnota, kolik bytů zabírá v paměti datový typ, jehož je ukazatel, v tomto případě int. Pokud tedy typ int má velikost 4 byty, dostaneme při operaci ptr + 1, adresu na kterou ukazuje ptr posunutou o 4 byty dopředu, tedy přesně o jeden prvek typu int (poznámka: při odečítání dvou pointerů pak dostaneme rozdíl adres vydělený velikostí daného typu, nutno tedy pamatovat, že rozdíl dvou pointerů nevrací pointer, ale celé číslo v podobě hodnoty typu int, používá se například při zjištění rozdílu indexů dvou prvků v poli). Pokud chceme získat hodnotu uloženou na této adrese, můžeme použít klasickou dereferenci - *(ptr + 1). Existuje ale ještě jeden způsob, který je mnohem přehlednější a přívětivější a to zapsání hodnoty posunu do hranatých závorek za název pointeru - ptr[1].

*(ptr + 7) = 20;  //přiřazení hodnoty 20 na adresu, na kterou ukazuje ptr + 7 * velikost typu int
ptr[7] = 20;  //a toto je úplně to samé

int* addr = ptr + 7;  //přiřazení adresy na kterou ukazuje ptr + 7 * velikost typu int pointeru addr
addr = &ptr[7]  //a toto je úplně to samé

Jistě vám tento zápis něco připomíná. Přesně tímto způsobem totiž program přistupuje k jednotlivým prvkům polí. Název pole funguje jako pointer, a při jeho použití číslo v hranatých závorkách udává o kolik prvků se má adresa posunout. Mezi klasickým polem a pointerem jsou pouze dva drobné rozdíly. Zaprvé v případě použití operátoru sizeof na deklarované pole, dostaneme velikost celého pole a ne pouze velikost pointeru. Zadruhé pokud je deklarované pole součástí objektu, pak při kopírování tohoto objektu do jiného objektu, se implicitně (za předpokladu že není přetížen operátor = a není definován kopírovací konstruktor) překopíruje celé pole a ne pouze pointer. Tyto dva rozdíly nám ovšem nebrání používat klasické pointery k přístupu k polím, přičemž tato pole můžeme spravovat dynamicky. To přináší určité výhody, jednak můžeme velikost pole kdykoliv změnit, jednak můžeme pole dealokovat kdy budeme chtít, podle potřeb dané situace a můžeme k němu přistupovat odkudkoliv. Dynamická alokace a dealokace polí se realizuje pomocí operátorů new[] a delete[].

Operátor new[]

Tento operátor funguje stejně jako operátor new, s tím rozdílem, že místo jednoto prvku jich vytvoří několik. Tyto prvky jsou v paměti za sebou jeden vedle druhého a operátor new[] nám vrátí adresu prvního z nich, tedy pointer na začátek tohoto bloku paměti. Syntaxe je následující:

int* array = new int[100];  //vytvoření 100 prvků typu int a přiřazení adresy prvního z nich pointeru array, nebo-li vytvoření pole o 100 prvcích typu int
*array = 20;  //přiřazení hodnoty 20 prvku na adrese na kterou ukazuje array, nebo-li přiřazení hodnoty 20 prvnímu prvku pole
array[0] = 20;  //toto je to samé
array[54] = 1000;  //to samé, jen přiřazujeme hodnotu pětapadesátému prvku pole
array[100] = 1;  //chyba - poslední index pole je 99

Operátor new[] nám neumožňuje inicializovat prvky při vytváření, takže pokud chceme vytvořit pole objektů nějaké třídy, tato třída musí mít implementovaný bezparametrický konstruktor, jakýkoliv jiný konstruktor totiž operátor new[] zavolat nemůže.

class trida
{
  private:
    ...
  public:
    trida(int param){...}
    ...
};
//bezparametrický konstruktor chybí
...

trida* objekty;
objekty = new trida[30];  //toto není možné, třída nemá implementovaný konstruktor trida()

Pokud chceme aby předchozí kód fungoval, musí vypadat takto:

class trida
{
  private:
    ...
  public:
    trida(){...}  //bezparametrický konstruktor
    trida(int param){...}
    ...
};

...

trida* objekty;
objekty = new trida[30];  //vytvoření třiceti objektů třídy, zavolání bezparametrického konstruktoru každého z nich a přiřazení adresy prvního prvku pointeru objekty

Operátor delete[]

Operátor delete[] funguje v zásadě stejně jako operátor delete. Rozdíl mezi nimi je ten, že delete dealokuje paměť alokovanou operátorem new, delete[] dealokuje paměť alokovanou operátorem new[]. Nelze to prohodit, stejně tak není možné dealokovat operátorem delete jednotlivé prvky vytvořené operátorem new[]. Syntaxe je následující:

int* array = new int[velikost];
delete [] array;  //dealokuje všechny prvky pole, na jehož začátek ukazuje pointer array
trida* objekty = new trida[velikost];
delete [] objekty;  //dealokuje všechny prvky pole, na jehož začátek ukazuje pointer objekty, před tím samozřejmě zavolá jejich destruktory
...
delete array;  //chyba
delete array[10];  //chyba
delete objekty  //chyba

int* ptr = new int(100)
delete [] ptr;  //chyba
...
int* array = (int*)new char[velikost];
delete [] array;  //toto je samozřejmě také chyba, typ pointeru neodpovídá typu dat

Operátoru delete[] stačí předat pointer odpovídajícího typu, který ukazuje na začátek pole, které chceme dealokovat. Počet prvků si zjistí sám.


 

  Aktivity (2)

Článek pro vás napsal Lukáš Hruda (Luckin)
Avatar
...

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 


Miniatura
Všechny články v sekci
Pokročilé konstrukce v C++
Miniatura
Následující článek
Reference

 

 

Komentáře

Avatar
Samuel Bachar:

Zaujímavé :) diki

 
Odpovědět 9.10.2015 13:22
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 1 zpráv z 1.