5.9 — std::string_view (część 2)

W poprzednich lekcjach przedstawiliśmy dwa typy ciągów znaków: std::string (5.7 — Wprowadzenie do std::string) i std::string_view (5.8 — Wprowadzenie do std::string_view).

Ponieważ std::string_view to nasze pierwsze spotkanie z typem widoku, poświęcimy trochę czasu na dalsze omówienie go. Skoncentrujemy się na tym, jak bezpiecznie używać std::string_view i podamy kilka przykładów ilustrujących, w jaki sposób można go używać nieprawidłowo. Na zakończenie podamy kilka wskazówek, kiedy używać std::string vs std::string_view.

Wprowadzenie dla właścicieli i widzów

Powróćmy na chwilę do analogii. Załóżmy, że zdecydowałeś, że namalujesz obraz przedstawiający rower. Ale ty nie masz roweru! Co masz zrobić?

Możesz pójść do lokalnego sklepu rowerowego i kupić taki. Miałbyś ten rower. Ma to pewne zalety: masz teraz rower, na którym możesz jeździć. Możesz mieć pewność, że rower będzie zawsze dostępny, kiedy tylko będziesz go potrzebować. Można go ozdobić lub przenieść. Ten wybór ma też pewne wady. Rowery są drogie. A jeśli go kupisz, jesteś teraz za niego odpowiedzialny. Trzeba go okresowo konserwować. A kiedy w końcu zdecydujesz, że już go nie chcesz, musisz się go odpowiednio pozbyć.

Własność może być kosztowna. Jako właściciel masz obowiązek nabywać, zarządzać i właściwie utylizować posiadane przedmioty.

Wychodząc z domu, spoglądasz przez okno. Zauważasz, że Twój sąsiad zaparkował rower naprzeciwko Twojego okna. Zamiast tego możesz po prostu namalować zdjęcie roweru sąsiada (widzianego z okna). Ten wybór niesie ze sobą wiele korzyści. Oszczędzasz wydatki związane z koniecznością zakupu własnego roweru. Nie musisz tego utrzymywać. Nie jesteś także odpowiedzialny za jego utylizację. Kiedy skończysz oglądać, możesz po prostu zasunąć zasłony i żyć dalej. To kończy twój widok obiektu, ale nie ma to wpływu na sam obiekt. Wybór ten ma również pewne potencjalne wady. Nie możesz malować ani dostosowywać roweru sąsiada. A kiedy będziesz oglądać rower, Twój sąsiad może zdecydować o zmianie wyglądu roweru lub całkowicie zniknąć Ci z pola widzenia. Zamiast tego możesz ujrzeć coś nieoczekiwanego.

Oglądanie jest niedrogie. Jako przeglądarka nie ponosisz odpowiedzialności za obiekty, które oglądasz, ale nie masz też kontroli nad tymi obiektami.

std::string jest (jedynym) właścicielem

Możesz się zastanawiać, dlaczego std::string tworzy kosztowną kopię swojego inicjatora. Po utworzeniu instancji obiektu przydzielana jest mu pamięć w celu przechowywania wszelkich danych, których potrzebuje przez cały okres istnienia obiektu. Ta pamięć jest zarezerwowana dla obiektu i gwarantuje, że będzie istnieć tak długo, jak obiekt będzie istniał. Jest to bezpieczna przestrzeń. std::string (i większość innych obiektów) kopiuje otrzymaną wartość inicjalizacyjną do tej pamięci, dzięki czemu mogą mieć własną niezależną wartość, do której można później uzyskać dostęp i którymi można manipulować. Po skopiowaniu wartości inicjującej obiekt nie jest już w żaden sposób zależny od inicjatora.

I to dobrze, ponieważ po zakończeniu inicjalizacji generalnie nie można ufać inicjatorowi. Jeśli wyobrażasz sobie proces inicjalizacji jako wywołanie funkcji inicjującej obiekt, kto przekazuje inicjator? Rozmówca. Po zakończeniu inicjalizacji kontrola wraca do osoby wywołującej. W tym momencie instrukcja inicjalizacji jest zakończona i zazwyczaj ma miejsce jedna z dwóch rzeczy:

  • Jeśli inicjatorem była wartość lub obiekt tymczasowy, ten tymczasowy zostanie natychmiast zniszczony.
  • Jeśli inicjatorem była zmienna, osoba wywołująca nadal ma dostęp do tego obiektu. Osoba wywołująca może następnie zrobić z obiektem co chce, łącznie z jego modyfikacją lub zniszczeniem.

Kluczowa informacja

Inicjowany obiekt nie ma kontroli nad tym, co stanie się z wartością inicjującą po zakończeniu inicjalizacji.

Ponieważ std::string tworzy własną kopię inicjatora, nie musi się martwić o to, co stanie się z inicjatorem po zakończeniu inicjalizacji. Inicjator może zostać zniszczony lub zmodyfikowany i nie ma to wpływu na std::string. Wadą jest to, że ta niezależność wiąże się z kosztem wykonania kosztownej kopii.

W kontekście naszej analogii std::string jest właścicielem -- jest odpowiedzialny za uzyskanie danych w postaci ciągu od inicjatora, zarządzanie dostępem do danych ciągu i prawidłowe pozbycie się danych ciągu w przypadku std::string obiektu zostanie zniszczony.

Kluczowa informacja

W programowaniu, gdy nazywamy obiekt właścicielem, zwykle mamy na myśli, że jest on jedynym właścicielem (o ile nie określono inaczej). Wyłączna własność (zwana także pojedynczą własnością) gwarantuje, że jasne jest, kto jest odpowiedzialny za te dane.

Nie zawsze potrzebujemy kopii

Powróćmy do przykładu z poprzedniej lekcji:

#include <iostream>
#include <string>

void printString(std::string str) // str makes a copy of its initializer
{
    std::cout << str << '\n';
}

int main()
{
    std::string s{ "Hello, world!" };
    printString(s);

    return 0;
}

Po wywołaniu printString(s) str wykonuje kosztowną kopię s. Funkcja wypisuje skopiowany ciąg, a następnie go niszczy.

Zauważ to s przetrzymuje już ciąg, który chcemy wydrukować. Czy moglibyśmy po prostu użyć trzymanego s ciągu znaków, zamiast tworzyć kopię? Odpowiedź jest prawdopodobnie -- istnieją trzy kryteria, które musimy ocenić:

  • Można s może zostać zniszczony, gdy str nadal z niej korzysta? Nie, str umiera na końcu funkcji i s istnieje w zasięgu wywołującego i nie może zostać zniszczona przed powrotem funkcji.
  • Można s zmienić<<<M19>>>w czasie str nadal z niej korzysta? Nie, str umiera na końcu funkcji, a osoba wywołująca nie ma możliwości modyfikacji s zanim funkcja powróci.
  • Czy str modyfikuje ciąg znaków w sposób, którego osoba wywołująca się nie spodziewa? Nie, funkcja w ogóle nie modyfikuje ciągu znaków.

Ponieważ wszystkie trzy kryteria są fałszywe, nie ma ryzyka użycia łańcucha, który s zamiast tworzenia kopii jest przechowywany. A skoro kopie ciągów znaków są drogie, po co płacić za takie, których nie potrzebujemy?

std::string_view Czy przeglądarka

std::string_view podchodzi inaczej do inicjalizacji. Zamiast tworzyć kosztowną kopię ciągu inicjującego, std::string_view tworzy niedrogi widok z łańcucha inicjującego. std::string_view można następnie użyć, gdy wymagany jest dostęp do ciągu znaków.

W kontekście naszej analogii std::string_view jest przeglądarką. Wyświetla obiekt, który już istnieje gdzie indziej i nie może go modyfikować. Kiedy widok zostanie zniszczony, nie ma to wpływu na oglądany obiekt. Jednoczesne oglądanie obiektu przez wielu widzów jest w porządku.

Ważne jest, aby pamiętać, że obiekt a std::string_view pozostaje zależny od inicjatora przez cały okres jego istnienia. Jeśli oglądany ciąg znaków zostanie zmodyfikowany lub zniszczony, gdy widok jest nadal używany, nastąpi nieoczekiwane lub niezdefiniowane zachowanie.

Za każdym razem, gdy korzystamy z widoku, od nas zależy, czy takie możliwości nie wystąpią.

Ostrzeżenie

Widok zależy od oglądanego obiektu. Jeżeli oglądany obiekt zostanie zmodyfikowany lub zniszczony w czasie, gdy widok jest nadal używany, nastąpi nieoczekiwane lub niezdefiniowane zachowanie.

A std::string_view oglądający zniszczony ciąg znaków jest czasami nazywany widokiem wiszącym widok.

std::string_view najlepiej używać jako parametru funkcji tylko do odczytu

Najlepiej używać std::string_view jako parametru funkcji tylko do odczytu. To pozwala nam przekazać ciąg znaków w stylu C, std::string lub std::string_view argument bez tworzenia kopii, ponieważ std::string_view utworzy widok na argument.

#include <iostream>
#include <string>
#include <string_view>

void printSV(std::string_view str) // now a std::string_view, creates a view of the argument
{
    std::cout << str << '\n';
}

int main()
{
    printSV("Hello, world!"); // call with C-style string literal

    std::string s2{ "Hello, world!" };
    printSV(s2); // call with std::string

    std::string_view s3 { s2 };
    printSV(s3); // call with std::string_view
       
    return 0;
}

Ponieważ str parametr funkcji jest tworzony, inicjowany, używany i niszczony przed powrotem kontroli do wywołującego, nie ma ryzyka, że oglądany ciąg (argument funkcji) zostanie zmodyfikowany lub zniszczony przed naszym str .

Czy powinienem wolisz std::string_view lub const std::string& parametry funkcji? Zaawansowane

Preferuj std::string_view w większości przypadków. Omówimy ten temat w dalszej części lekcji 12.6 -- Przekaż stałą lwartość odniesienia.

Niewłaściwe użycie std::string_view

Przyjrzyjmy się kilku przypadkom, w których niewłaściwe użycie std::string_view wpędza nas w kłopoty.

Oto nasz pierwszy przykład:

#include <iostream>
#include <string>
#include <string_view>

int main()
{
    std::string_view sv{};

    { // create a nested block
        std::string s{ "Hello, world!" }; // create a std::string local to this nested block
        sv = s; // sv is now viewing s
    } // s is destroyed here, so sv is now viewing an invalid string

    std::cout << sv << '\n'; // undefined behavior

    return 0;
}

W tym przykładzie tworzymy std::string s wewnątrz zagnieżdżonego bloku (nie martw się jeszcze, czym jest zagnieżdżony blok). Następnie ustawiamy sv aby wyświetlić s. s jest on następnie niszczony na końcu zagnieżdżonego bloku. sv nie wie, że s został zniszczony. Kiedy następnie używamy sv, uzyskujemy dostęp do nieprawidłowego obiektu, co skutkuje niezdefiniowanym zachowaniem.

Oto inny wariant tego samego problemu, w którym inicjujemy a std::string_view za pomocą std::string zwracanej wartości funkcji:

#include <iostream>
#include <string>
#include <string_view>

std::string getName()
{
    std::string s { "Alex" };
    return s;
}

int main()
{
  std::string_view name { getName() }; // name initialized with return value of function
  std::cout << name << '\n'; // undefined behavior

  return 0;
}

Zachowuje się to podobnie do poprzedniego przykładu. Funkcja getName() zwraca std::string zawierający ciąg znaków „Alex”. Zwracane wartości to obiekty tymczasowe, które są niszczone na końcu pełnego wyrażenia zawierającego wywołanie funkcji. Musimy albo natychmiast użyć tej zwracanej wartości, albo skopiować ją do użycia później.

Ale std::string_view nie tworzy kopii. Zamiast tego tworzy widok tymczasowej wartości zwracanej, która jest następnie niszczona. To pozostawia nasz std::string_view zawieszony (oglądanie nieprawidłowego obiektu), a wydrukowanie widoku skutkuje niezdefiniowanym zachowaniem.

Poniższy jest mniej oczywisty wariant powyższego:

#include <iostream>
#include <string>
#include <string_view>

int main()
{
    using namespace std::string_literals;
    std::string_view name { "Alex"s }; // "Alex"s creates a temporary std::string
    std::cout << name << '\n'; // undefined behavior

    return 0;
}

A std::string literał (utworzony za pomocą s sufiksu literału) tworzy tymczasowy std::string obiekt. Zatem w tym przypadku "Alex"s tworzy tymczasowy std::string, którego następnie używamy jako inicjatora dla name. W tym momencie name przegląda tymczasowe std::string. Następnie tymczasowe std::string jest niszczone, pozostawiając name zwisające. Otrzymamy niezdefiniowane zachowanie, gdy następnie użyjemy name.

Ostrzeżenie

Nie inicjuj literału std::string_view z std::string , ponieważ spowoduje to pozostawienie std::string_view zawieszone.

. Można zainicjować std::string_view literałem ciągu w stylu C lub literałem std::string_view . Można także zainicjować std::string_view obiektem tekstowym w stylu C, obiektem std::string lub obiektem std::string_view , o ile ten obiekt tekstowy przetrwa dłużej niż widok.

Możemy również uzyskać niezdefiniowane zachowanie, gdy zmodyfikowany zostanie podstawowy ciąg znaków:

#include <iostream>
#include <string>
#include <string_view>

int main()
{
    std::string s { "Hello, world!" };
    std::string_view sv { s }; // sv is now viewing s

    s = "Hello, a!";    // modifies s, which invalidates sv (s is still valid)
    std::cout << sv << '\n';   // undefined behavior

    return 0;
}

W tym przykładzie sv jest ponownie ustawiany na widok s. s jest następnie modyfikowany. Kiedy a std::string zostanie zmodyfikowany, wszelkie poglądy na ten std::string prawdopodobnie będą unieważniają, co oznacza, że ​​te poglądy są teraz nieważne lub nieprawidłowe. Użycie unieważnionego widoku spowoduje niezdefiniowane zachowanie.

Dla zaawansowanych czytelników

  • Jeśli std::string ponownie przydziela pamięć, aby pomieścić nowe dane ciągów, zwróci pamięć używaną dla starych danych ciągów z powrotem do systemu operacyjnego. Ponieważ std::string_view wciąż przegląda stare dane ciągu, teraz jest zawieszony (wskazując na obecnie nieprawidłowy obiekt).
  • Jeśli std::string nie dokonuje ponownej alokacji pamięci, kopiuje nowe dane ciągu na stare dane ciągu (zaczynając od tego samego adresu pamięci). std::string_view będzie teraz przeglądał nowe dane ciągu (ponieważ został umieszczony pod tym samym adresem pamięci, co przeglądał), ale nie zorientuje się, że długość std::string prawdopodobnie się zmieniła. Jeśli nowy ciąg jest dłuższy niż stary ciąg, std::string_view będzie teraz przeglądał podciąg nowego ciągu (o tej samej długości co stary ciąg). Jeśli nowy ciąg jest krótszy niż stary, std::string_view będzie teraz przeglądał superciąg nowego ciągu (składający się z całego nowego ciągu plus wszelkie znaki niepotrzebne, które nadal znajdują się w pamięci za końcem ciągu).

Kluczowa informacja

Modyfikowania std::string prawdopodobnie unieważni wszystkie widoki na ten ciąg std::string.

Ponowne sprawdzanie poprawności niepoprawny std::string_view

Unieważnione obiekty można często ponownie zweryfikować (przywrócić ich ważność) poprzez przywrócenie ich do znanego dobrego stanu. W przypadku unieważnionego std::string_view możemy to zrobić, przypisując unieważnionemu std::string_view obiektowi prawidłowy ciąg znaków do wyświetlenia.

Oto ten sam przykład co poprzednio, ale ponownie sprawdzimy sv:

#include <iostream>
#include <string>
#include <string_view>

int main()
{
    std::string s { "Hello, world!" };
    std::string_view sv { s }; // sv is now viewing s

    s = "Hello, universe!";    // modifies s, which invalidates sv (s is still valid)
    std::cout << sv << '\n';   // undefined behavior

    sv = s;                    // revalidate sv: sv is now viewing s again
    std::cout << sv << '\n';   // prints "Hello, universe!"

    return 0;
}

Po sv unieważnieniu przez modyfikację s, ponownie zatwierdzić sv za pomocą instrukcji sv = s, która powoduje, że sv staje się ważnym poglądem s ponownie. Kiedy drukujemy sv po raz drugi, wypisuje „Witaj, wszechświecie!”.

Uważaj, zwracając std::string_view

std::string_view można użyć jako wartości zwracanej przez funkcję. Jest to jednak często niebezpieczne.

Ponieważ zmienne lokalne są niszczone na końcu funkcji, zwrócenie std::string_view wyświetlającego zmienną lokalną spowoduje, że zwrócona std::string_view jest nieprawidłowa, a dalsze użycie tej std::string_view zaowocuje niezdefiniowanym zachowaniem. Na przykład:

#include <iostream>
#include <string>
#include <string_view>

std::string_view getBoolName(bool b)
{
    std::string t { "true" };  // local variable
    std::string f { "false" }; // local variable

    if (b)
        return t;  // return a std::string_view viewing t

    return f; // return a std::string_view viewing f
} // t and f are destroyed at the end of the function

int main()
{
    std::cout << getBoolName(true) << ' ' << getBoolName(false) << '\n'; // undefined behavior

    return 0;
}

W powyższym przykładzie, gdy getBoolName(true) jest wywoływane, funkcja zwraca std::string_view to, że przeglądanie t. Jednak t jest niszczone na końcu funkcji. Oznacza to, że zwracany std::string_view ogląda obiekt, który został zniszczony. Zatem po wydrukowaniu zwróconego std::string_view uzyskuje się niezdefiniowane zachowanie.

Twój kompilator może ostrzegać Cię o takich przypadkach lub nie.

Istnieją dwa główne przypadki, w których a std::string_view można bezpiecznie zwrócić. Po pierwsze, ponieważ literały łańcuchowe w stylu C istnieją dla całego programu, dobrze jest (i jest to przydatne) zwracanie literałów łańcuchowych w stylu C z funkcji, która ma typ zwracany std::string_view.

#include <iostream>
#include <string_view>

std::string_view getBoolName(bool b)
{
    if (b)
        return "true";  // return a std::string_view viewing "true"

    return "false"; // return a std::string_view viewing "false"
} // "true" and "false" are not destroyed at the end of the function

int main()
{
    std::cout << getBoolName(true) << ' ' << getBoolName(false) << '\n'; // ok

    return 0;
}

Wypisuje:

true false

Po wywołaniu getBoolName(true) , funkcja zwróci std::string_view wyświetlając ciąg w stylu C "true". Ponieważ "true" istniejący dla całego programu, nie ma problemu, gdy używamy zwróconego std::string_view print "true" w main().

Po drugie, ogólnie jest w porządku zwróć parametr funkcji typu std::string_view:

#include <iostream>
#include <string>
#include <string_view>

std::string_view firstAlphabetical(std::string_view s1, std::string_view s2)
{
    if (s1 < s2)
        return s1;
    return s2;
}

int main()
{
    std::string a { "World" };
    std::string b { "Hello" };

    std::cout << firstAlphabetical(a, b) << '\n'; // prints "Hello"

    return 0;
}

Może być mniej oczywiste, dlaczego jest to w porządku. Po pierwsze, zauważ, że argumenty a i b istnieją w zasięgu obiektu wywołującego. Gdy funkcja jest wywoływana, parametr funkcji s1 jest widokiem w a, a parametr funkcji s2 jest widokiem w b. Gdy funkcja zwraca s1 lub s2, zwraca widok a lub b z powrotem do wywołującego. Ponieważ a i b nadal istnieje w tym momencie, można użyć zwróconego std::string_view do a lub b.

Jest tu jedna ważna subtelność. Jeżeli argumentem jest obiekt tymczasowy (który zostanie zniszczony na końcu pełnego wyrażenia zawierającego wywołanie funkcji), w tym samym wyrażeniu należy zastosować wartość zwracaną std::string_view . Po tym momencie element tymczasowy zostaje zniszczony, a std::string_view pozostaje nieaktualny.

Ostrzeżenie

Jeśli argument jest tymczasowy i ulega zniszczeniu na końcu pełnego wyrażenia zawierającego wywołanie funkcji, zwrócony std::string_view musi zostać użyty natychmiast, ponieważ pozostanie nieaktywny po zniszczeniu tymczasowego.

Wyświetl funkcje modyfikacji

Rozważ okno w swoim domu, patrząc na samochód elektryczny stojący na ulicy. Możesz spojrzeć przez okno i zobaczyć samochód, ale nie możesz go dotknąć ani poruszyć. Twoje okno zapewnia po prostu widok na samochód, który jest zupełnie odrębnym obiektem.

Wiele okien posiada zasłony, które pozwalają nam modyfikować nasz widok. Możemy zamknąć lewą lub prawą kurtynę, aby ograniczyć to, co widzimy. Nie zmieniamy tego, co na zewnątrz, po prostu zmniejszamy widoczny obszar.

Ponieważ std::string_view jest widokiem, zawiera funkcje, które pozwalają modyfikować nasz widok poprzez „zasłonięcie zasłon”. Nie modyfikuje to w żaden sposób wyświetlanego ciągu znaków, tylko sam widok.

  • Klasa remove_prefix() Funkcja członkowska usuwa znaki z lewej strony widoku.
  • Klasa remove_suffix() Funkcja członkowska usuwa znaki z prawej strony widoku.
#include <iostream>
#include <string_view>

int main()
{
	std::string_view str{ "Peach" };
	std::cout << str << '\n';

	// Remove 1 character from the left side of the view
	str.remove_prefix(1);
	std::cout << str << '\n';

	// Remove 2 characters from the right side of the view
	str.remove_suffix(2);
	std::cout << str << '\n';

	str = "Peach"; // reset the view
	std::cout << str << '\n';

	return 0;
}

Ten program generuje następujące dane wyjściowe:

Peach
each
ea
Peach

W przeciwieństwie do prawdziwych zasłon, po wywołaniu remove_prefix() i remove_suffix() jedynym sposobem zresetowania widoku jest ponowne przypisanie do niego ciągu źródłowego ponownie.

std::string_view można wyświetlić podciąg

To pokazuje ważne zastosowanie std::string_view. Chociaż std::string_view można użyć do wyświetlenia całego łańcucha bez tworzenia kopii, są one również przydatne, gdy chcemy wyświetlić podciąg bez tworzenia kopii. A podciąg jest ciągłą sekwencją znaków w istniejącym ciągu znaków. Na przykład, biorąc pod uwagę ciąg „śnieżka”, niektóre podciągi to „śnieg”, „wszystko” i „teraz”. „sowa” nie jest podciągiem „kuli śnieżnej”, ponieważ znaki te nie pojawiają się w „kuli śnieżnej” w sposób ciągły.

std::string_view może być zakończony znakiem null lub nie

Możliwość przeglądania tylko podciągu większego ciągu wiąże się z jedną konsekwencją: a std::string_view Może być zakończone znakiem null lub nie.

Powiązana treść

Co to jest ciąg zakończony znakiem null, omówimy w lekcji 5.2 -- Literały.

Rozważmy ciąg „kuli śnieżnej”, który jest zakończony znakiem null (ponieważ jest to literał ciągu w stylu C, który zawsze jest zakończony znakiem null). Jeśli a std::string_view wyświetla cały ciąg znaków, to przegląda ciąg znaków zakończony znakiem null. Jeśli jednak std::string_view wyświetla tylko podciąg „teraz”, wówczas podciąg ten nie jest zakończony znakiem null (następnym znakiem jest „b”).

Kluczowa informacja

Literał ciągu w stylu C i a std::string są zawsze zakończone znakiem zerowym.
A std::string_view Może być zakończone znakiem null lub nie.

W prawie wszystkich przypadkach nie ma to znaczenia -- a std::string_view śledzi długość łańcucha lub podciągu, który przegląda, więc nie potrzebuje terminatora zerowego. Konwersja a std::string_view do std::string będzie działać niezależnie od tego, czy std::string_view jest zakończony znakiem null.

Ostrzeżenie

Uważaj, aby nie napisać żadnego kodu, który zakłada, że a std::string_view jest zakończony znakiem null.

Wskazówka

Jeśli masz ciąg znaków zakończony znakiem null std::string_view i z jakiegoś powodu potrzebujesz łańcucha zakończonego znakiem null, przekonwertuj std::string_view do std::string.

Krótki przewodnik, kiedy używać std::string vs std::string_view

Ten przewodnik nie jest wyczerpujący, ale ma na celu omówienie najczęstszych przypadków:

Zmienne

Użyj std::string zmienna, gdy:

  • Potrzebujesz ciągu znaków, który możesz modyfikować.
  • Musisz przechowywać tekst wprowadzony przez użytkownika.
  • Musisz zapisać wartość zwracaną funkcji, która zwraca a std::string.

Użyj std::string_view zmienna, gdy:

  • Potrzebujesz dostępu tylko do odczytu do części lub całości ciągu, który już istnieje gdzie indziej i nie zostanie zmodyfikowany ani zniszczony przed zakończeniem użycia std::string_view .
  • Potrzebujesz stałej symbolicznej dla ciągu w stylu C.
  • Musisz kontynuować przeglądanie wartości zwracanej przez funkcję, która zwraca ciąg w stylu C lub non-dangling std::string_view.

Parametry funkcji

Użyj std::string parametr funkcji, gdy:

  • Funkcja musi zmodyfikować ciąg znaków przekazany jako argument bez wpływu na osobę wywołującą. Jest to rzadkie.
  • Używasz standardu języka C++14 lub starszego i nie czujesz się jeszcze komfortowo w korzystaniu z referencji.

Użyj std::string_view parametr funkcji, gdy:

  • Funkcja wymaga ciągu tylko do odczytu.
  • Funkcja musi działać z ciągami znaków zakończonymi znakiem null.

Dla zaawansowanych czytelników

Zobacz także parametr funkcji 12.6 -- Przekaż stałą lwartość odniesienia.

Użyj const std::string& parametr funkcji, gdy:

  • Używasz standardu języka C++14 lub starszego, a funkcja wymaga do działania ciągu tylko do odczytu (ponieważ std::string_view nie jest dostępny aż do C++17).
  • wywołujesz inne funkcje, które wymagają const std::string, const std::string& lub stałego ciągu w stylu C (ponieważ std::string_view może nie być zakończony znakiem null).

Użyj std::string& parametr funkcji, gdy:

Typy zwracane

Użyj std::string gdy:

  • Wartością zwracaną jest std::string zmienna lokalna lub parametr funkcji.
  • Wartością zwracaną jest wywołanie funkcji lub operator, który zwraca std::string według wartości.

Użyj std::string_view gdy:

  • Funkcja zwraca literał ciągu w stylu C lub lokalny std::string_view który został zainicjowany ciągiem w stylu C dosłownie.
  • Funkcja zwraca std::string_view .

Dla zaawansowanych czytelników

Zobacz lekcję 12.12 -- Powrót przez referencję i powrót przez adres więcej informacji na temat zwracanych typów referencyjnych.

Użyj std::string_view gdy:

  • Zapisanie akcesora dla std::string_view członka.

Użyj std::string& gdy:

  • Funkcja zwraca std::string& .

Użyj const std::string& gdy:

  • Funkcja zwraca const std::string& .
  • Zapisanie akcesora dla std::string lub const std::string członka.
  • Funkcja zwraca wartość statyczną (lokalną lub globalną) const std::string.

Wglądy

Rzeczy do zapamiętania std::string:

  • Inicjowanie i kopiowanie std::string jest kosztowne, więc unikaj tego w miarę możliwości.
  • Unikaj przekazywania std::string wartości, ponieważ spowoduje to utworzenie kopii.
  • Jeśli to możliwe, unikaj tworzenia krótkotrwałych std::string obiekty.
  • Modyfikowania std::string spowoduje unieważnienie wszelkich widoków tego ciągu.
  • Zwrócenie lokalnego std::string według wartości.

Rzeczy do zapamiętania std::string_view:

  • std::string_view jest zwykle używane do przekazywania parametrów funkcji łańcuchowych i zwracania literałów łańcuchowych.
  • Ponieważ literały łańcuchowe w stylu C istnieją dla całego programu, zawsze dobrze jest ustawić std::string_view na ciąg w stylu C dosłownie.
  • Kiedy łańcuch zostanie zniszczony, wszystkie widoki tego ciągu zostaną unieważnione.
  • Użycie unieważnionego widoku (innego niż użycie przypisania do ponownej walidacji widoku) spowoduje niezdefiniowane zachowanie.
  • A std::string_view Może być zakończone znakiem null lub nie.
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:  
296 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze