std::string: tekst, który sam sobą zarządza
Na poprzedniej stronie widać było, jak inteligentne wskaźniki pozwalają obiektowi posiadać zasób i automatycznie po nim sprzątać. std::string to ta sama idea zastosowana do tekstu: posiada bufor znaków, powiększa go, gdy dopisujesz, i zwalnia, gdy napis wychodzi z zasięgu. Nigdy nie wywołujesz new, nie liczysz bajtów i nie martwisz się o brakujący terminator null.
Aby go użyć, dołącz <string>. Klasa należy do przestrzeni nazw std.
Operator + wykonuje tu prawdziwą pracę: alokuje nowy bufor wystarczająco duży na oba kawałki i kopiuje je do niego. Przy surowym char*, czyli zwykłym wskaźniku do bufora znaków, trzeba by sięgać po strcpy i strcat z nadzieją, że bufor jest wystarczająco duży. std::string sprawia, że cała ta klasa błędów znika.
Budowanie i łączenie napisów
Łączyć można przez +, ale podstawowym narzędziem do powiększania napisu w miejscu jest +=. Dopisuje do istniejącego bufora, zamiast za każdym razem tworzyć zupełnie nowy napis, co ma znaczenie w pętlach.
Częste zaskoczenie: + wymaga, żeby przynajmniej jeden operand już był std::string. Dwa literały tekstowe to tylko const char*, więc "a" + "b" się nie kompiluje: nie ma operator+ dla dwóch surowych wskaźników. Najpierw zrób z jednego z nich string:
string s = "a" + "b"; // error: can't add two const char*
string s = string("a") + "b"; // fine - left side is a std::string
string s = "a"s + "b"; // fine in C++14+, the "s" literal suffix
Ostatnia forma używa przyrostka s z <string> (using namespace std::string_literals;), który zamienia literał bezpośrednio w std::string.
Sięganie do wnętrza napisu
std::string zachowuje się jak kontener char, więc możesz go indeksować, przechodzić po nim pętlą i prosić o jego fragmenty.
substr(pos, len) zwraca zupełnie nowy napis skopiowany z oryginału; nie zmienia źródła. Uważaj na granice: word[10] na napisie o 5 znakach to niezdefiniowane zachowanie: nie rzuci wyjątku, tylko przeczyta śmieci. Jeśli chcesz dostępu ze sprawdzaniem, który rzuca std::out_of_range, użyj word.at(10) zamiast word[10].
Kolejna klasyczna pułapka: .size() zwraca typ bez znaku (size_t). Pętla wsteczna w rodzaju for (size_t i = word.size() - 1; i >= 0; --i) nigdy się nie kończy, bo i bez znaku nigdy nie spadnie poniżej zera, tylko przekręci się do ogromnej liczby. Gdy musisz przejść napis od końca, użyj indeksu ze znakiem albo przebuduj warunek.
Wyszukiwanie i zamiana
find odnajduje podnapis albo znak i zwraca indeks początku. Gdy szukanego elementu nie ma, zwraca specjalną stałą std::string::npos: zawsze porównuj z nią, zamiast zakładać, że find zwraca -1.
Aby zmienić tekst w miejscu, replace(pos, len, text) zamienia fragment na nową treść (która może mieć inną długość), a insert/erase dodają albo usuwają kawałki:
Napisy i liczby
Tekst i liczby nie konwertują się same: "42" to trzy znaki, a nie liczba całkowita 42. Biblioteka standardowa daje funkcje konwersji w obie strony. Używaj stoi, stod i pokrewnych do parsowania tekstu na liczby, a to_string do konwersji w drugą stronę.
Są tu dwie pułapki. Po pierwsze stoi("abc") rzuca std::invalid_argument, więc niezaufane dane zabezpieczaj blokiem try/catch. Po drugie to_string(3.99) daje pełne 3.990000: jeśli potrzebujesz kontroli nad precyzją i formatowaniem, to zadanie dla strumieni napisów, do których właśnie zmierzamy.
try {
int n = std::stoi(userInput);
} catch (const std::invalid_argument&) {
std::cout << "That wasn't a number.\n";
}
Dalej: wejście i wyjście
Napisy wypisujesz przez cout od samego początku, ale wczytywanie ich od użytkownika ma własne niespodzianki: cin >> name zatrzymuje się na pierwszej spacji, więc cała linia, taka jak „Ada Lovelace”, wymaga zamiast tego std::getline. Następna strona porządnie omawia wejście i wyjście w C++: operatory strumieni, wczytywanie całych linii i łączenie >> z getline bez potykania się o pozostałe znaki nowej linii.
Najczęściej zadawane pytania
Czym się różni std::string od char* w C++?
std::string to klasa, która posiada własną pamięć i nią zarządza, rośnie w razie potrzeby i sprząta po sobie automatycznie. char* to tylko surowy wskaźnik na znaki zakończone '\0': sam zarządzasz buforem, długością i czasem życia. Prawie do wszystkiego wybieraj std::string; usuwa całe kategorie błędów przepełnienia bufora i wiszących wskaźników.
Jak poznać długość stringa w C++?
Wywołaj .size() albo jego alias .length() na std::string: name.size() zwraca liczbę znaków jako size_t. Obie metody zwracają tę samą wartość; size() to częstszy styl, bo pasuje do wszystkich innych kontenerów STL.
Jak zamienić string na liczbę w C++?
Użyj std::stoi dla int, std::stod dla double i podobnych (stol, stof). Przykład: int n = std::stoi("42");. Te funkcje rzucają std::invalid_argument, jeśli tekst nie jest liczbą, więc gdy dane wejściowe nie są zaufane, otocz je blokiem try/catch.