Czy tablice powinny być używane w C ++?

96

Ponieważ std::listi std::vectoristnieją, czy istnieje powód, aby używać tradycyjnych tablic C w C ++, czy też należy ich unikać, tak jak malloc?

Andreas
źródło
18
@Als: To pytanie dotyczy różnicy między dwoma określonymi kontenerami, podczas gdy to pytanie dotyczy ogólnej różnicy między surowymi tablicami a standardowymi kontenerami.
Jon Purdy

Odpowiedzi:

109

W C ++ 11 tam, gdzie std::arrayjest dostępna, odpowiedź brzmi „tak, należy unikać tablic”. Przed C ++ 11 może być konieczne użycie tablic C do przydzielenia tablic w automatycznym magazynie (tj. Na stosie).

dasblinkenlight
źródło
3
Jednak wiele kompilatorów nadal nie obsługuje C ++ 11. Będziesz musiał zdecydować, kiedy lepiej użyć jednego, a nie drugiego, biorąc pod uwagę brak std :: array
Nowayz
4
std :: array to szablon, który wpływa na duże projekty pod względem czasu kompilacji i prawdopodobnie rozmiaru kodu, ponieważ dla każdej kombinacji T, N szablon jest tworzony od nowa.
zvrba,
std :: vector gwarantuje wyrównanie danych w standardzie, więc może być używany prawie wszędzie. W C ++ 11 naprawdę nie ma powodu, aby używać tablic w C.
Nils,
16
Tablice @Nils również gwarantują wyrównanie. Ponadto automatyczne przydzielanie pamięci („stos”) jest znacznie szybsze niż dynamiczne przydzielanie pamięci. Jeśli wiem, że mam dokładnie 3 elementy [np. Współrzędne trójkąta], nie ma powodu, aby używać wektora.
zvrba,
9
@zvrba - sprawdź wygenerowany zestaw przy użyciu tablic std :: array vs C. Żadnej różnicy.
Nemanja Trifunovic
85

Zdecydowanie, chociaż std::arrayw C ++ 11, praktycznie tylko dla danych statycznych. Tablice w stylu C mają trzy ważne zalety w stosunku do std::vector:

  • Nie wymagają alokacji dynamicznej. Z tego powodu tablice w stylu C powinny być preferowane, gdy istnieje duże prawdopodobieństwo, że masz dużo bardzo małych tablic. Powiedz coś w rodzaju punktu n-wymiaru:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };

    Zwykle można sobie wyobrazić, że dimsbędzie to bardzo małe (2 lub 3), Twbudowany typ ( double) i może mieć std::vector<Point>miliony elementów. Na pewno nie chcesz milionów dynamicznych alokacji 3-krotnych.

  • Obsługa inicjalizacji statycznej. Jest to problem tylko dla danych statycznych, gdzie coś takiego jak:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };

    Jest to często korzystniejsze wektor (a std::string), gdyż unika wszystko kolejność inicjalizacji zagadnień; dane są wstępnie ładowane, zanim będzie można wykonać jakikolwiek kod.

  • Wreszcie, w związku z powyższym, kompilator może obliczyć rzeczywisty rozmiar tablicy z inicjatorów. Nie musisz ich liczyć.

Jeśli masz dostęp do C ++ 11, std::arrayrozwiązuje pierwsze dwa problemy i zdecydowanie powinien być używany w pierwszym przypadku zamiast tablic w stylu C. Nie dotyczy to jednak trzeciego, a wymiarowanie tablicy przez kompilator zgodnie z liczbą inicjatorów jest nadal ważnym powodem, aby preferować tablice w stylu C.

James Kanze
źródło
11
Inicjalizacja tablicy w stylu C eliminuje również potrzebę powtarzania się. int i[] = { 1, 2, 3 };kontynuuje pracę z int i[] = { 1, 2, 3, 4 };. array<int, 3>należy ręcznie zmienić na array<int, 4>.
10
@JoeWreschnig Zmiana, o której łatwo zapomnieć. Jeśli dodasz element, kompilator powinien narzekać, ale jeśli usuniesz jeden, na końcu otrzymasz dodatkowy, zainicjowany element o wartości 0. Nadal intensywnie używam tablic w stylu C dla tego rodzaju danych statycznych.
James Kanze
3
Pierwsze zdanie nie ma sensu.
Konrad Rudolph
4
Trzeci punkt można rozwiązać dość elegancko, używając make_arrayfunkcji podobnej do make_pairitp . Nakładka kapelusza na @R. Martinho Fernandes .
Konrad Rudolph
@KonradRudolph: Jasne, że tak. Andreas pyta „Czy tablice powinny być używane w C ++?”, Na co James odpowiada: „Zdecydowanie, chociaż std::arrayw C ++ 11 [powinny być używane] praktycznie tylko dla danych statycznych”.
Jon Purdy
15

Nigdy nie mów „nigdy”, ale zgodzę się, że ich rola jest znacznie ograniczona przez prawdziwe struktury danych z STL.

Powiedziałbym również, że hermetyzacja wewnątrz obiektów powinna zminimalizować wpływ takich wyborów. Jeśli tablica jest prywatnym składnikiem danych, możesz ją wymieniać lub wchodzić bez wpływu na klientów swojej klasy.

duffymo
źródło
11

Pracowałem nad systemami krytycznymi dla bezpieczeństwa, w których nie można używać dynamicznej alokacji pamięci. Pamięć zawsze musi znajdować się na stosie. Dlatego w tym przypadku użyjesz tablic, ponieważ rozmiar jest ustalany w czasie kompilacji.

Ed Heal
źródło
8
Przed C ++ 11 zgodziłbym się, ale std::array<T>alokuje na stosach i zasadniczo nie ma narzutu na surową tablicę.
111111
5
@ 111111 - Zgoda. Ale wiem, że niektórzy ludzie z tej branży nie przeszli jeszcze na C ++ 11
Ed Heal
Wiem, że dlatego nie przegłosowałem cię, ale myślę, że boost miał swoją wersję i łatwo jest również wyrzucić własną.
111111
6
ale w systemach krytycznych dla bezpieczeństwa nie używasz nowych funkcji kompilatora (mniej przetestowanych) i nie używasz boost.
James Kanze
3
Wiele systemów krytycznych dla bezpieczeństwa jest zbudowanych na STARYCH kompilatorach, które nie mają nawet nowszych funkcji kompilatora, ponieważ zmiana łańcuchów narzędzi jest powolnym i kosztownym procesem, który wymaga mnóstwa dokumentacji, testów i certyfikatów.
Brian McFarland
6

arrayin c++zapewnia szybką alternatywę dynamicznych rozmiarów std::vectori std::list. std :: array jest jednym z dodatków w c++11. Zapewnia korzyści z kontenerów std, jednocześnie zapewniając semantykę typu agregacyjnego tablic w stylu C.

Więc z c++11pewnością użyłbym std::array, tam gdzie jest to wymagane, nad wektorem. Ale chciałbym uniknąć w stylu C tablicę C++03.

Vikas
źródło
4

Zwykle nie , nie przychodzi mi do głowy powód, dla którego miałbym używać tablic surowych, powiedzmy vectors. Jeśli kod jest nowy .

Być może będziesz musiał uciekać się do korzystania z tablic, jeśli twoje biblioteki muszą być zgodne z kodem, który oczekuje tablic i surowych wskaźników.

Luchian Grigore
źródło
1
... ale od C ++ 03 wektor „faktycznie ma” tablicę, do której można uzyskać dostęp za pomocą wskaźnika w celu odczytu lub zapisu. To obejmuje większość przypadków kodu, który oczekuje wskaźników do tablic. Dopiero wtedy, gdy ten kod przydziela lub zwalnia tablicę, nie można użyć wektora.
Steve Jessop
@SteveJessop czy możesz uzyskać dostęp do wewnętrznej tablicy?
Luchian Grigore
1
@LuchianGrigore: vector.data()w C ++ 11 lub &vector.front()wcześniej.
Mike Seymour
@Luchian: pod warunkiem, że wektor nie jest pusty, możesz wziąć wskaźnik do elementu (a jeśli jest pusty, możesz przekazać pusty wskaźnik i długość 0 do dowolnej rozsądnie napisanej funkcji, która akceptuje wielkość liter w przypadku bufor o rozmiarze zerowym). Prawie jedynym celem gwarancji ciągłości wektorów dodanej w C ++ 03 było umożliwienie używania wektorów jako buforów przez kod zorientowany na wskaźnik.
Steve Jessop
1
@SteveJessop I fakt, że wiele osób i tak uważało, że jest to gwarantowane, i uważano, że lepiej ich nie zawieść.
James Kanze
4

Wiem, że wiele osób wskazuje std :: array do przydzielania tablic na stosie i std :: vector dla stosu. Ale żaden z nich nie wydaje się wspierać wyrównania obcego. Jeśli robisz dowolny kod numeryczny, na którym chcesz użyć instrukcji SSE lub VPX (wymagając odpowiednio wyrównania 128 lub 256 bajtów), tablice C nadal wydają się najlepszym rozwiązaniem.

gct
źródło
3

Powiedziałbym, że tablice są nadal przydatne, jeśli przechowujesz niewielką statyczną ilość danych, dlaczego nie.

james82345
źródło
2

Jedyną zaletą tablicy (oczywiście zapakowanej w coś, co automatycznie zarządza jej cofnięciem alokacji w razie potrzeby), nad std::vectorktórą mogę pomyśleć, jest to, że vectornie może przekazać własności swoich danych, chyba że twój kompilator obsługuje C ++ 11 i konstruktory przenoszenia.

Tadeusz Kopeć
źródło
6
„wektor nie może przenieść własności swoich danych” - tak, może w C ++ 03 przy użyciu swap.
Steve Jessop
2

Tablice w stylu C są podstawową strukturą danych, więc będą przypadki, kiedy lepiej z niej skorzystać. Jednak w przypadku ogólnym użyj bardziej zaawansowanych struktur danych, które zaokrągla rogi danych bazowych. C ++ pozwala robić bardzo interesujące i użyteczne rzeczy z pamięcią, z których wiele działa z prostymi tablicami.

James Wynn
źródło
3
W jaki sposób tablice w stylu C są bardziej fundamentalne niż std::arrays? W wielu przypadkach oba zostaną skompilowane do tego samego zestawu.
lewej stronie około
1
Bardziej fundamentalne, ponieważ jest bardziej podstawowe. Wiesz, co będzie robić tablica, std :: array może mieć dziwactwa implementacyjne, ponieważ opiera się na standardowej bibliotece.
James Wynn
1
@JamesWynn Niezupełnie. std::arrayma precyzyjnie zdefiniowaną semantykę zbudowaną na podstawie tablic statycznych.
Konrad Rudolph
1

Powinieneś używać kontenerów STL wewnętrznie, ale nie powinieneś przekazywać wskaźników do takich kontenerów między różnymi modułami, w przeciwnym razie skończysz w piekle zależności. Przykład:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

to bardzo dobre rozwiązanie, ale nie

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

Powodem jest to, że std :: string można zaimplementować na wiele różnych sposobów, ale łańcuch w stylu c jest zawsze łańcuchem w stylu C.

user877329
źródło
Myślę, że próbujesz powiedzieć: nie łącz ze sobą różnych kodów obiektowych, jeśli do ich tworzenia zostały użyte różne kompilatory / implementacje biblioteki standardowej. To z pewnością prawda. Jak to się ma do pierwotnego pytania?
jogojapan
To tylko wskazówka, kiedy należy używać tablic lub kontenerów STL. Twórz dane za pomocą kontenera i przekazuj je jako tablicę. W przypadku innych danych, które są łańcuchami, miałbyś coś takiego jak myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329
Ale te problemy pojawiają się tylko wtedy, gdy łączysz różne implementacje biblioteki standardowej w tym samym projekcie. To szalone. W każdym normalnym fragmencie kodu przekazanie, powiedzmy, wektora przez odniesienie (lub w C ++ 11, przeniesienie go) do funkcji jest całkowicie w porządku.
jogojapan
1
Po prostu lubię wtyczki
user877329,