Zpracování řetězce v C ++ - C++ string handling

C ++ programovací jazyk, má podporu pro manipulaci s řetězci , hlavně realizované v jeho standardní knihovně . Jazyková norma specifikuje několik typů řetězců, některé zděděné z jazyka C , některé navržené pro využití funkcí jazyka, jako jsou třídy a RAII . Nejpoužívanější z nich je std :: string .

Vzhledem k tomu, že původní verze C ++ měly pouze funkce a konvence zpracování řetězců C „nízké úrovně“, bylo v průběhu let navrženo několik nekompatibilních návrhů pro třídy manipulace s řetězci a stále se místo nich používají std::stringa programátoři C ++ možná budou muset zpracovat více konvencí v jedné aplikaci.

Dějiny

Typ std :: string je hlavním datovým typem řetězce ve standardním C ++ od roku 1998, ale nebyl vždy součástí C ++. Z C C ++ zdědilo konvenci používání řetězců zakončených nulou, které jsou zpracovávány ukazatelem na jejich první prvek, a knihovnu funkcí, které s takovými řetězci manipulují. V moderním standardu C ++ řetězcový literál, například „ahoj“, stále označuje pole znaků zakončené NUL.

Použití tříd C ++ k implementaci typu řetězce nabízí několik výhod automatizované správy paměti a snížené riziko přístupů mimo hranice a intuitivnější syntaxi pro porovnání a zřetězení řetězců. Proto bylo velmi lákavé vytvořit takovou třídu. V průběhu let vytvořili vývojáři aplikací, knihoven a rozhraní C ++ své vlastní nekompatibilní řetězcové reprezentace, jako je ta v knihovně AT&T Standard Components (první taková implementace, 1983) nebo typ CString v MFC společnosti Microsoft . Zatímco standardizované řetězce std :: string , starší aplikace stále běžně obsahují takové vlastní typy řetězců a knihovny mohou očekávat řetězce ve stylu C, takže je „prakticky nemožné“ vyhnout se používání více typů řetězců v programech C ++ a vyžadovat, aby programátoři rozhodli o požadovaném řetězci zastoupení před zahájením projektu.

V retrospektivě k historii C ++ z roku 1991 označil její vynálezce Bjarne Stroustrup nedostatek standardního typu řetězce (a některých dalších standardních typů) v C ++ 1.0 za nejhorší chybu, kterou udělal při svém vývoji; „jejich absence vedla k tomu, že všichni znovu vynalezli kolo a ke zbytečné rozmanitosti v nejzákladnějších třídách“.

Problémy s implementací

Typy řetězců různých dodavatelů mají různé implementační strategie a výkonnostní charakteristiky. Zejména některé typy řetězců používají strategii kopírování při zápisu , kde operace, jako je

string a = "hello!";
string b = a; // Copy constructor

ve skutečnosti nekopíruje obsah a do b ; místo toho oba řetězce sdílejí svůj obsah a je zvýšen počet odkazů na obsah. Skutečné kopírování se odkládá, dokud se pomocí operace mutace, jako je přidání znaku k jednomu řetězci, obsah řetězců neliší. Copy-on-write can make major performance changes to code using strings (making some operations much faster and some much slower). Ačkoli ji std :: string již nepoužívá, mnoho (možná většina) alternativních knihoven řetězců stále implementuje řetězce copy-on-write.

Některé implementace řetězců ukládají 16bitové nebo 32bitové body kódu namísto bajtů, což mělo usnadnit zpracování textu Unicode . To však znamená, že převod na tyto typy ze std :: string nebo z matic bajtů je pomalá a často ztrátová operace, závislá na „národním prostředí“, a může vyvolat výjimky. Při zavedení kódování UTF-16 s proměnnou šířkou zmizly všechny výhody zpracování 16bitových kódových jednotek (i když stále existují výhody, pokud musíte komunikovat s 16bitovým API, jako je Windows). Příkladem je Qt 's QString .

Implementace řetězců třetích stran se také značně lišila v syntaxi pro extrakci nebo porovnání podřetězců nebo prohledávání textu.

Standardní typy řetězců

Třída std :: string je standardní reprezentací textového řetězce od C ++ 98 . Třída poskytuje několik typických řetězcových operací, jako je porovnání, zřetězení, hledání a nahrazování a funkce pro získání podřetězců . Std :: string může být zhotovena z C-stylu řetězec a řetězec C-style mohou být také získány z jednoho.

Jednotlivé jednotky tvořící řetězec jsou typu char , minimálně (a téměř vždy) každá 8 bitů. V moderním použití to často nejsou „znaky“, ale části vícebajtového kódování znaků, jako je UTF-8 .

Strategie copy-on-write byla záměrně povolena počátečním standardem C ++ pro std :: string, protože byla považována za užitečnou optimalizaci a byla používána téměř všemi implementacemi. Vyskytly se však chyby, zejména operátor [] vrátil nekonstantní odkaz, aby bylo snadné přenášet C manipulace s řetězci na místě (takový kód často předpokládal jeden bajt na znak, a proto to nemusí být dobrý To umožnilo následující kód, který ukazuje, že musí vytvořit kopii, i když se téměř vždy používá pouze k prozkoumání řetězce a ne k jeho úpravám:

  std::string original("aaaaaaa");
  std::string string_copy = original; // make a copy
  char* pointer = &string_copy[3]; // some tried to make operator[] return a "trick" class but this makes it complex
  arbitrary_code_here(); // no optimizations can fix this
  *pointer = 'b'; // if operator[] did not copy, this would change original unexpectedly

To způsobilo, že některé implementace opustily kopírování při zápisu. Bylo také zjištěno, že režie ve vícevláknových aplikacích kvůli zamykání potřebnému k prozkoumání nebo změně počtu odkazů byla větší než režie kopírování malých řetězců na moderních procesorech (zejména pro řetězce menší než velikost ukazatele). Optimalizace byla nakonec v C ++ 11 zakázána , což mělo za následek, že i předání std :: řetězce jako argumentu funkci, viz.

void print(std::string s) { std::cout << s; }

musí se očekávat provedení úplné kopie řetězce do nově přidělené paměti. Běžným idiomem, jak se vyhnout takovému kopírování, je předat jako referenci const :

void print(const std::string& s) { std::cout << s; }

V C ++ 17 přidal novou třídu string_view, která je pouze ukazatelem a délkou dat jen pro čtení, umožňuje předávání argumentů mnohem rychlejší než některý z výše uvedených příkladů:

void print(std::string_view s) { std::cout << s; }
...
  std::string x = ...;
  print(x); // does not copy x.data()
  print("this is a literal string"); // also does not copy the characters!
...


Příklad použití

#include <iostream>
#include <iomanip>
#include <string>

int main() {
    std::string foo = "fighters";
    std::string bar = "stool";
    if (foo != bar) std::cout << "The strings are different!\n";
    std::cout << "foo = " << std::quoted(foo)
              << " while bar = " << std::quoted(bar);
}

Související třídy

std :: string je typedef pro konkrétní instanci třídy šablony std :: basic_string . Jeho definice se nachází v záhlaví <string> :

using string = std::basic_string<char>;

Řetězec tedy poskytuje funkci basic_string pro řetězce, které mají prvky typu char . Existuje podobná třída std :: wstring , která se skládá z wchar_t a nejčastěji se používá k ukládání textu UTF-16 ve Windows a UTF-32 na většině platforem podobných Unixu . Standard C ++ však na tyto typy neukládá žádnou interpretaci jako body kódu Unicode nebo jednotky kódu a nezaručuje ani to, že wchar_t pojme více bitů než char . Chcete-li některé z neslučitelnosti vyplývající z wchar_t s vlastnostmi, C ++ 11 přidány dvě nové třídy: std :: u16string a std :: u32string (vyrobené z nových typů char16_t a char32_t ), které jsou vzhledem k tomu, počet bitů na jednotku kódu na všech platformách. C ++ 11 také přidal nové řetězcové literály 16bitových a 32bitových „znaků“ a syntaxi pro vkládání kódových bodů Unicode do řetězců zakončených nulou (ve stylu C).

Basic_string je zaručeno, že specializable pro jakýkoli typ s char_traits struct ji doprovázejí. Jako C ++ 11, tak char , wchar_t , char16_t a char32_t specializace musí být prováděna ve standardní knihovně; všechny ostatní typy jsou definovány implementací. Každá specializace je také kontejnerem standardní knihovny , a proto lze algoritmy standardní knihovny použít na jednotky kódu v řetězcích.

Kritiky

Návrh std :: string byl ukázán jako příklad monolitického designu Herbem Sutterem , který počítá s tím, že ze 103 členských funkcí třídy ve třídě C ++ 98, 71 by bylo možné oddělit bez ztráty účinnosti implementace.

Reference