std::array

Mit std::array verfügt die Standardbibliothek über eine zweite Art von Array neben std::vector, die allerdings ein paar kleine interessante Nebenbedingungen hat.

Video

Quellcode

#include <iostream>
#include <array>
#include <vector>

int main()
{
    std::vector<int> v1 { 1, 2, 3 };
    std::vector<int> v2 { 4, 5, 6 };

    std::array<int, 3> a1 { 1, 2, 3 };
    std::array<int, 3> a2 { 4, 5, 6 };

    auto iv1 = v1.begin();
    auto iv2 = v2.begin();

    auto ia1 = a1.begin();
    auto ia2 = a2.begin();

    std::cout << "v1: " << *iv1 << ", v2: " << *iv2 << " - ";
    std::cout << "a1: " << *ia1 << ", a2: " << *ia2 << std::endl;

    ++iv1; ++iv2;
    ++ia1; ++ia2;
    std::cout << "v1: " << *iv1 << ", v2: " << *iv2 << " - ";
    std::cout << "a1: " << *ia1 << ", a2: " << *ia2 << std::endl;

    v1.swap(v2);
    a1.swap(a2);
    std::cout << "v1: " << *iv1 << ", v2: " << *iv2 << " - ";
    std::cout << "a1: " << *ia1 << ", a2: " << *ia2 << std::endl;

    ++iv1; ++iv2;
    ++ia1; ++ia2;
    std::cout << "v1: " << *iv1 << ", v2: " << *iv2 << " - ";
    std::cout << "a1: " << *ia1 << ", a2: " << *ia2 << std::endl;
}

Erklärung

Der Grundgedanke von std::array ist die Überführung von "normalen" C-Arrays in die Welt der Standardbibliothek. Für alle praktischen Belange sollen sich Objekte des Templates verhalten, wie ein normales Array. Dazu gehört bspw. auch, dass sie komplett auf dem Stack allokiert werden können. std::vector macht das unmöglich. Die zum Vergrößern des internen Speichers ist es zwingend notwendig, dass dynamisch Speicher allokiert werden kann. Damit steht fest, dass dieser Speicher eigentlich immer auf dem Heap liegen muss, da eine dynamische Allokierung nunmal dort stattfindet. Auf dem Stack liegt im Zweifelsfall nur das std::vector-Objekt.

std::array ist da anders: zuersteinmal haben Objekte der Klasse eine feste größe, die als Templateparameter übergeben wird. Das ermöglicht es dem Compiler, das zugrundeliegende Array als Feld fester Größe in das eigentliche Klassenobjekt einzubauen. Damit liegt natürlich dieses Array auch auf dem Stack, wenn das Objekt dort liegt. Das Ganze halt allerdings einen Preis: std::array kann seine Größe zur Laufzeit nicht ändern.

Ein zweiter Preis: führt man einen swap zwischen zwei Objekten durch (also das Austauschen der Inhalte), dann wird das bei std::vector in O(1) stattfinden. Im häufigsten Fall werden einfach die beiden Pointer auf die dynamisch allokierten Speicherbereiche ausgetauscht und fertig. Größe des Vektors: egal. Im Falle von std::array geht das nicht. Da der Speicherbereich Bestandteil des Objektes ist, kann er nicht mal eben an ein anderes Objekt abgegeben werden. Daher müssen die Objekte im Array tatsächlich kopiert werden: swap ist also in O(n). Ein zweites Problem ist das leicht unterschiedliche Verhalten der Iteratoren: Iteratoren in std::vector bleiben bei einem Swap bei den Daten. Tauschen also im Beispiel oben v1 und v2 ihre Daten, dann tauschen sie auch ihre bestehenden Iteratoren. Bei std::array ist das wieder anders: die Iteratoren bleiben beim Array (in dem Sinne eigentlich auch beim Datenbereich, der ja nicht ausgetauscht wird). Sie zeigen also nach einem swap auf andere Werte. Ein kleiner, aber feiner Unterschied, wenn man Iteratoren zwischenspeichert und ggf. mit swap arbeitet. Welches Verhalten jetzt hier erwartbarer ist, darüber kann man sich streiten. Ich persönlich war von dem Verhalten von std::vector schon einigermaßen überrascht.