Biblioteka standardowa zawiera wiele przydatnych klas - ale być może najbardziej użyteczną jest std::string. std::string (i std::wstring) to klasa łańcuchów, która udostępnia wiele operacji przypisywania, porównywania i modyfikowania ciągów. W tym rozdziale przyjrzymy się szczegółowo tym klasom ciągów.
Uwaga: Ciągi w stylu C będą nazywane „ciągami w stylu C”, podczas gdy std::string (i std::wstring) będą nazywane po prostu „łańcuchami”.
Nota autora
Ten rozdział jest nieco przestarzały i prawdopodobnie zostanie skondensowany w przyszłej aktualizacji. Możesz przeglądać materiał w poszukiwaniu pomysłów i przydatnych przykładów, ale w celu uzyskania najbardziej aktualnych informacji należy preferować strony z odniesieniami technicznymi (np. cppreference).
Motywacja dla klasy string
W poprzedniej lekcji omówiliśmy Ciągów w stylu C, który wykorzystuje tablice char do przechowywania ciągu znaków. Jeśli próbowałeś zrobić cokolwiek z ciągami w stylu C, bardzo szybko dojdziesz do wniosku, że praca z nimi jest uciążliwa, łatwo je zepsuć i trudno debugować.
Łańcuchy w stylu C mają wiele wad, związanych przede wszystkim z faktem, że musisz sam zarządzać całą pamięcią. Na przykład, jeśli chcesz przypisać ciąg „witaj!” do bufora, musisz najpierw dynamicznie przydzielić bufor o odpowiedniej długości:
char* strHello { new char[7] };Nie zapomnij uwzględnić dodatkowego znaku dla terminatora zerowego!
Następnie musisz faktycznie skopiować wartość do:
strcpy(strHello, "hello!");Mam nadzieję, że bufor jest wystarczająco duży, aby nie doszło do przepełnienia bufora!
I oczywiście, ponieważ ciąg znaków jest przydzielany dynamicznie, musisz to zrobić pamiętaj o prawidłowym zwolnieniu alokacji, gdy już skończysz:
delete[] strHello;Nie zapomnij użyć usuwania tablicy zamiast normalnego usuwania!
Co więcej, wiele intuicyjnych operatorów dostępnych w C do pracy z liczbami, takich jak przypisanie i porównania, po prostu nie działa z ciągami w stylu C. Czasami wydaje się, że działają, ale w rzeczywistości dają nieprawidłowe wyniki — na przykład porównanie dwóch ciągów w stylu C za pomocą == w rzeczywistości spowoduje porównanie wskaźników, a nie porównanie ciągów. Przypisanie jednego ciągu w stylu C do innego za pomocą operator= będzie początkowo działać, ale w rzeczywistości powoduje wykonanie kopii wskaźnika (płytka kopia), co generalnie nie jest tym, czego chcesz. Tego typu rzeczy mogą prowadzić do awarii programów, które są bardzo trudne do znalezienia i debugowania!
Konkluzja jest taka, że praca z ciągami znaków w stylu C wymaga zapamiętania wielu drobiazgowych zasad dotyczących tego, co jest bezpieczne/niebezpieczne, zapamiętywania kilku funkcji o śmiesznych nazwach, takich jak strcat() i strcmp() zamiast używania intuicyjnych operatorów, a także wykonywania wielu ręcznych operacji zarządzania pamięcią.
Na szczęście C++ i standardowa biblioteka zapewniają znacznie lepszy sposób radzenia sobie z ciągami znaków: klasy std::string i std::wstring. Wykorzystując pojęcia C++, takie jak konstruktory, destruktory i przeciążanie operatorów, std::string umożliwia tworzenie ciągów znaków i manipulowanie nimi w intuicyjny i bezpieczny sposób! Koniec z zarządzaniem pamięcią, koniec z dziwnymi nazwami funkcji i znacznie zmniejszone ryzyko katastrofy.
Zarejestruj się!
Przegląd ciągów znaków
Wszystkie funkcje ciągów w standardowej bibliotece znajdują się w
#include <string>W nagłówku ciągu znajdują się właściwie 3 różne klasy ciągów. Pierwsza to szablonowa klasa bazowa o nazwie basic_string<>:
namespace std
{
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string;
}Nie będziesz pracować z tą klasą bezpośrednio, więc nie martw się na razie o cechy lub alokator. Wartości domyślne wystarczą w prawie każdym możliwym przypadku.
Istnieją dwie wersje basic_string<> dostarczane przez standardową bibliotekę:
namespace std
{
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
}To są dwie klasy, których będziesz faktycznie używać. std::string jest używany dla standardowych ciągów znaków ascii i utf-8. std::wstring jest używany do ciągów znaków dwuznakowych/unicode (utf-16). Nie ma wbudowanej klasy dla ciągów utf-32 (chociaż powinieneś móc rozszerzyć własną z basic_string<>, jeśli jej potrzebujesz).
Chociaż będziesz bezpośrednio używać std::string i std::wstring, cała funkcjonalność ciągów jest zaimplementowana w klasie basic_string<>. String i wstring mają bezpośredni dostęp do tej funkcjonalności dzięki szablonowi. W związku z tym wszystkie przedstawione funkcje będą działać zarówno dla stringów, jak i wstringów. Jednakże, ponieważ basic_string jest klasą szablonową, oznacza to również, że kompilator będzie generował okropnie wyglądające błędy szablonu, gdy zrobisz coś niepoprawnego składniowo z ciągiem znaków lub wstringiem. Nie daj się zastraszyć tymi błędami; wyglądają znacznie gorzej niż są w rzeczywistości!
Oto lista wszystkich funkcji w klasie string. Większość tych funkcji ma wiele odmian do obsługi różnych typów danych wejściowych, które omówimy bardziej szczegółowo w następnych lekcjach.
| Funkcja | Efekt |
|---|---|
| (konstruktor) (destruktor) | Utwórz lub skopiuj ciąg znaków Zniszcz ciąg |
| capacity() empty() length(), size() max_size() reserve() | Zwraca liczbę znaków, które mogą być przechowywane bez realokacji Zwraca wartość logiczną wskazującą, czy ciąg znaków jest pusty Zwraca liczbę znaków w ciągu Zwraca maksymalny rozmiar łańcucha, który można przydzielić Zwiększ lub zmniejsz pojemność ciągu |
| [], at() | Dostęp do znaku w określonym miejscu indeks |
| =, przypisanie() +=, append(), push_back() insert() clear() erase() replace() resize() swap() | Przypisuje nową wartość do ciągu Łączy znaki koniec ciągu Wstawia znaki pod dowolnym indeksem w ciągu Usuń wszystkie znaki w ciągu Usuń znaki pod dowolnym indeksem w ciągu Zamień znaki o dowolnym indeksie na inne znaki Rozwiń lub zmniejsz ciąg (obcina lub dodaje znaki na końcu string) Zamiana wartości dwóch ciągów znaków |
| >>, getline() << c_str() copy() data() | Odczytuje wartości ze strumienia wejściowego do string Zapisuje wartość ciągu do strumienia wyjściowego Zwraca zawartość łańcucha w postaci łańcucha w stylu C zakończonego znakiem NULL Kopiuje zawartość (niezakończoną znakiem NULL) do tablicy znaków Tak samo jak c_str(). Przeciążenie inne niż const umożliwia zapis do zwróconego ciągu. |
| ==, != <, <=, > >= compare() | Porównuje, czy dwa ciągi są równe/nierówne (zwraca wartość bool) Porównuje, czy dwa ciągi są mniejsze/większe od siebie (zwraca wartość bool) Porównuje, czy dwa ciągi są równe/nierówne (zwraca -1, 0 lub 1) |
| + substr() | Łączy dwa ciągi znaków Zwraca podciąg |
| find() find_first_of() find_first_ not_of() find_last_of() find_last_not_of() rfind() | Znajdź indeks pierwszego znaku/podciągu Znajdź indeks pierwszego znaku ze zbioru znaków Znajdź indeks pierwszego znaku nie ze zbioru znaków Znajdź indeks ostatniego znaku ze zbioru znaków Znajdź indeks ostatniego znaku nie ze zbioru znaków Znajdź indeks ostatniego znaku znak/podciąg |
| begin(), end() get_allocator() rbegin(), rend() | Obsługa iteratora w kierunku do przodu dla początku/końca łańcucha Zwraca alokator Obsługa iteratora w odwrotnym kierunku dla początku/końca łańcucha string |
Chociaż standardowe klasy ciągów bibliotecznych zapewniają dużą funkcjonalność, istnieje kilka godnych uwagi pominięć:
- Konstruktory do tworzenia ciągów znaków z liczb
- Funkcje wielkich/wielkich/małych liter
- Porównania bez uwzględniania wielkości liter
- Tokenizacja / dzielenie ciągu na tablicę
- Łatwe funkcje uzyskiwania lewa lub prawa część ciągu
- Przycinanie białych znaków
- Formatowanie łańcucha w stylu sprintf
- Konwersja z utf-8 na utf-16 lub odwrotnie
W przypadku większości z nich będziesz musiał albo napisać własne funkcje, albo przekonwertować ciąg na ciąg w stylu C (przy użyciu c_str()) i skorzystać z funkcji C, które oferują tę funkcjonalność.
W następnych lekcjach przyjrzymy się bardziej szczegółowo różnym funkcjom klasy string. Chociaż w naszych przykładach będziemy używać stringów, wszystko ma również zastosowanie do wstring.

