19,2 — Dynamiczne przydzielanie tablic

Oprócz dynamicznego przydzielania pojedynczych wartości, możemy również dynamicznie przydzielać tablice zmiennych. W przeciwieństwie do tablicy o stałym rozmiarze, gdzie rozmiar tablicy musi być ustalony w czasie kompilacji, dynamiczna alokacja tablicy pozwala nam wybrać długość tablicy w czasie wykonywania (co oznacza, że nasza długość nie musi być constexpr).

Nota autora

W tych lekcjach będziemy dynamicznie alokować tablice w stylu C, które są najpopularniejszym typem tablic alokowanych dynamicznie.

Chociaż możesz dynamicznie alokować std::array, w tym przypadku zwykle lepiej jest użyć alokacji niedynamicznej std::vector .

Aby alokować tablicę dynamicznie, używamy form tablicowych new i Delete (często nazywanych new[] i Delete[]):

#include <cstddef>
#include <iostream>

int main()
{
    std::cout << "Enter a positive integer: ";
    std::size_t length{};
    std::cin >> length;

    int* array{ new int[length]{} }; // use array new.  Note that length does not need to be constant!

    std::cout << "I just allocated an array of integers of length " << length << '\n';

    array[0] = 5; // set element 0 to value 5

    delete[] array; // use array delete to deallocate array

    // we don't need to set array to nullptr/0 here because it's going out of scope immediately after this anyway

    return 0;
}

Ponieważ alokujemy tablicę, C++ wie, że powinien używać tablicy w wersji new zamiast skalarnej wersji new. Zasadniczo wywoływany jest operator new[], mimo że [] nie jest umieszczony obok słowa kluczowego new.

Długość dynamicznie alokowanych tablic ma typ std::size_t. Jeśli używasz int innego niż constexpr, będziesz musiał static_cast Do std::size_t ponieważ jest to uważane za konwersję zawężającą, a kompilator wyświetli ostrzeżenie w przeciwnym razie.

Zauważ, że ponieważ ta pamięć jest alokowana z innego miejsca niż pamięć używana dla stałych tablic, rozmiar tablicy może być dość duży. Możesz uruchomić powyższy program i bez problemu przydzielić tablicę o długości 1 000 000 (lub prawdopodobnie nawet 100 000 000). Spróbuj! Z tego powodu programy, które muszą alokować dużo pamięci w C++, zwykle robią to dynamicznie.

Dynamiczne usuwanie tablic

Podczas usuwania dynamicznie alokowanej tablicy musimy użyć wersji tablicowej usuwania, czyli usuwania[].

To informuje procesor, że musi wyczyścić wiele zmiennych zamiast pojedynczej. Jednym z najczęstszych błędów popełnianych przez nowych programistów podczas dynamicznej alokacji pamięci jest użycie metody usuwania zamiast usuwania [] podczas usuwania dynamicznie alokowanej tablicy. Użycie skalarnej wersji usuwania tablicy spowoduje niezdefiniowane zachowanie, takie jak uszkodzenie danych, wycieki pamięci, awarie lub inne problemy.

Jednym z często zadawanych pytań dotyczących usuwania tablicy[] jest: „Skąd usuwanie tablicy wie, ile pamięci należy usunąć?” Odpowiedź jest taka, że ​​tablica new[] śledzi, ile pamięci przydzielono zmiennej, dzięki czemu tablica Delete[] może usunąć odpowiednią ilość. Niestety programista nie ma dostępu do tego rozmiaru/długości.

Tablice dynamiczne są prawie identyczne z tablicami stałymi

W lekcji 17.8 -- Zanikanie tablicy w stylu C, dowiedziałeś się, że stała tablica przechowuje adres pamięci pierwszego elementu tablicy. Nauczyłeś się również, że stała tablica może rozpaść się na wskaźnik wskazujący pierwszy element tablicy. W tej rozłożonej formie długość stałej tablicy nie jest dostępna (a zatem nie jest również dostępny rozmiar tablicy za pomocą sizeof()), ale poza tym różnica jest niewielka.

Tablica dynamiczna zaczyna swoje życie jako wskaźnik wskazujący na pierwszy element tablicy. W związku z tym ma te same ograniczenia, ponieważ nie zna swojej długości ani rozmiaru. Tablica dynamiczna działa identycznie jak uszkodzona stała tablica, z tą różnicą, że programista jest odpowiedzialny za zwolnienie tablicy dynamicznej za pomocą słowa kluczowego Delete[].

Inicjowanie tablic alokowanych dynamicznie

Jeśli chcesz zainicjować tablicę alokowaną dynamicznie na 0, składnia jest dość prosta:

int* array{ new int[length]{} };

Przed C++11 nie było łatwego sposobu na zainicjowanie tablicy dynamicznej na wartość różna od zera (listy inicjatorów działały tylko w przypadku stałych tablic). Oznacza to, że trzeba było przeglądać tablicę i jawnie przypisywać wartości elementów.

int* array = new int[5];
array[0] = 9;
array[1] = 7;
array[2] = 5;
array[3] = 3;
array[4] = 1;

Bardzo irytujące!

Jednakże, począwszy od C++11, możliwe jest teraz inicjowanie tablic dynamicznych przy użyciu list inicjatorów!

int fixedArray[5] = { 9, 7, 5, 3, 1 }; // initialize a fixed array before C++11
int* array{ new int[5]{ 9, 7, 5, 3, 1 } }; // initialize a dynamic array since C++11
// To prevent writing the type twice, we can use auto. This is often done for types with long names.
auto* array{ new int[5]{ 9, 7, 5, 3, 1 } };

Zauważ, że w tej składni nie ma operatora= pomiędzy długością tablicy a listą inicjatorów.

Dla zachowania spójności, stałe tablice można również inicjować przy użyciu jednolitej inicjalizacji:

int fixedArray[]{ 9, 7, 5, 3, 1 }; // initialize a fixed array in C++11
char fixedArray[]{ "Hello, world!" }; // initialize a fixed array in C++11

Jasne określenie rozmiaru tablicy: opcjonalne.

Zmiana rozmiaru tablic

Dynamiczna alokacja tablicy pozwala na ustawienie długości tablicy w momencie alokacji. Jednak C++ nie zapewnia wbudowanego sposobu zmiany rozmiaru tablicy, która została już przydzielona. Można obejść to ograniczenie poprzez dynamiczne przydzielanie nowej tablicy, kopiowanie elementów i usuwanie starej tablicy. Jest to jednak podatne na błędy, zwłaszcza gdy typem elementu jest klasa (która rządzi się specjalnymi zasadami regulującymi sposób ich tworzenia).

W związku z tym zalecamy unikanie robienia tego samodzielnie. Użyj std::vector .

Czas quizu

Pytanie nr 1

Napisz program, który:

std::string obsługuje porównywanie ciągów za pomocą operatorów porównania < i >. Nie musisz implementować ręcznego porównywania ciągów.

Twoje dane wyjściowe powinny pasować do tego:

How many names would you like to enter? 5
Enter name #1: Jason
Enter name #2: Mark
Enter name #3: Alex
Enter name #4: Chris
Enter name #5: John

Here is your sorted list:
Name #1: Alex
Name #2: Chris
Name #3: Jason
Name #4: John
Name #5: Mark

Przypomnienie

Możesz użyć std::getline() aby wczytać nazwy zawierające spacje (zobacz lekcja 5.7 — Wprowadzenie do std::string).

Przypomnienie

Aby użyć std::sort() ze wskaźnikiem do tablicy, oblicz ręcznie początek i koniec

std::sort(array, array + arrayLength);

Pokaż rozwiązanie

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
996 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze