C++ stränghantering
C++ standardbibliotek |
---|
Behållare |
C standardbibliotek |
Programmeringsspråket C++ har stöd för stränghantering , mestadels implementerat i dess standardbibliotek . Språkstandarden specificerar flera strängtyper, några ärvda från C , några utformade för att använda språkets funktioner, såsom klasser och RAII . Den mest använda av dessa är std::string .
Eftersom de första versionerna av C++ endast hade "lågnivå" C-stränghanteringsfunktioner och konventioner, har flera inkompatibla designs för stränghanteringsklasser designats under åren och används fortfarande istället för std::string
, och C++-programmerare kan behöva att hantera flera konventioner i en enda applikation.
Historia
Std ::strängtypen är huvudsträngens datatyp i standard C++ sedan 1998, men den var inte alltid en del av C++. Från C ärvde C++ konventionen att använda nollterminerade strängar som hanteras av en pekare till deras första element, och ett bibliotek med funktioner som manipulerar sådana strängar. I modern standard C++ betecknar en bokstavlig sträng som "hej" fortfarande en NUL-terminerad uppsättning tecken.
Att använda C++-klasser för att implementera en strängtyp erbjuder flera fördelar med automatiserad minneshantering och en minskad risk för out-of-bound-åtkomster, och mer intuitiv syntax för strängjämförelse och sammanlänkning. Därför var det starkt lockande att skapa en sådan klass. Under åren har C++-applikations-, biblioteks- och ramverksutvecklare producerat sina egna, inkompatibla strängrepresentationer, som den i AT&T :s standardkomponentbibliotek (den första implementeringen av detta slag, 1983) eller CString -typen i Microsofts MFC . Medan std::string standardiserade strängar, innehåller äldre applikationer fortfarande sådana anpassade strängtyper och bibliotek kan förvänta sig strängar i C-stil, vilket gör det "nästan omöjligt" att undvika att använda flera strängtyper i C++-program och kräver att programmerare ska bestämma sig för den önskade strängen representation inför start av ett projekt.
I en retrospektiv 1991 på historien om C++ kallade dess uppfinnare Bjarne Stroustrup avsaknaden av en standardsträngtyp (och några andra standardtyper) i C++ 1.0 för det värsta misstaget han gjorde i dess utveckling; "Frånvaron av dessa ledde till att alla återuppfann hjulet och till en onödig mångfald i de mest fundamentala klasserna".
Implementeringsfrågor
De olika leverantörernas strängtyper har olika implementeringsstrategier och prestandaegenskaper. Speciellt vissa strängtyper använder en kopiera-på-skriv- strategi, där en operation som t.ex
string a = "hej!" ; sträng b = a ; // Kopiera konstruktör
kopierar faktiskt inte innehållet i a till b ; istället delar båda strängarna sitt innehåll och en referensräkning på innehållet ökas. Själva kopieringen skjuts upp tills en muterande operation, som att lägga till ett tecken till endera strängen, gör att strängarnas innehåll skiljer sig åt. Copy-on-write kan göra stora prestandaförändringar i kod med hjälp av strängar (gör vissa operationer mycket snabbare och andra mycket långsammare). Även om std::string inte längre använder det, implementerar många (kanske de flesta) alternativa strängbibliotek fortfarande kopiera-på-skriv-strängar.
Vissa strängimplementeringar lagrar 16-bitars eller 32-bitars kodpunkter istället för bytes, detta var avsett att underlätta bearbetningen av Unicode- text. Det betyder dock att konvertering till dessa typer från std::string eller från arrayer av byte är beroende av "locale" och kan skapa undantag. Eventuella bearbetningsfördelar med 16-bitars kodenheter försvann när UTF-16- kodningen med variabel bredd introducerades (även om det fortfarande finns fördelar om du måste kommunicera med ett 16-bitars API som Windows). Qt :s QString är ett exempel.
Tredjeparts strängimplementationer skilde sig också avsevärt i syntaxen för att extrahera eller jämföra delsträngar, eller för att utföra sökningar i texten.
Standard strängtyper
Klassen std::string är standardrepresentationen för en textsträng sedan C++98 . Klassen tillhandahåller några typiska strängoperationer som jämförelse, sammanlänkning, hitta och ersätt och en funktion för att erhålla delsträngar . En std::sträng kan konstrueras från en C-sträng, och en C-sträng kan också erhållas från en.
De individuella enheterna som utgör strängen är av typen char , minst (och nästan alltid) 8 bitar vardera. I modern användning är dessa ofta inte "tecken", utan delar av en multibyte-teckenkodning som UTF-8 .
Kopiera-på-skriv-strategin tillåts medvetet av den ursprungliga C++-standarden för std::string eftersom den ansågs vara en användbar optimering och användes av nästan alla implementeringar. Det fanns dock misstag, i synnerhet operatören[] en icke-konstreferens för att göra det enkelt att porta C på plats strängmanipulationer (en sådan kod antog ofta en byte per tecken och därför kanske det inte var bra idé!) Detta tillät följande kod som visar att den måste göra en kopia även om den nästan alltid bara används för att undersöka strängen och inte ändra den:
std :: sträng original ( "aaaaaaa" ); std :: string string_copy = original ; // gör en kopia char * pointer = & string_copy [ 3 ]; // några försökte få operator[] att returnera en "trick"-klass men detta gör den komplex arbitrary_code_here (); // inga optimeringar kan fixa denna * pointer = 'b' ; // om operatorn[] inte kopierade, skulle detta ändra originalet oväntat
Detta orsakade vissa implementeringar [ vilka? ] för att överge copy-on-write. Det upptäcktes också att overheaden i flertrådade applikationer på grund av låsningen som behövs för att undersöka eller ändra referensräkningen var större än overheaden för kopiering av små strängar på moderna processorer (särskilt för strängar som är mindre än storleken på en pekare). Optimeringen förbjöds slutligen i C++11 , med resultatet att till och med skicka en std::string som ett argument till en funktion, dvs.
void print ( std :: string s ) { std :: cout << s ; }
måste förväntas utföra en fullständig kopia av strängen till nyligen allokerat minne. Det vanliga uttrycket för att undvika sådan kopiering är att passera som en konstreferens :
void print ( const std :: string & s ) { std :: cout << s ; }
I C++17 lades till en ny string_view- klass som bara är en pekare och längd för skrivskyddad data, vilket gör att argument skickas mycket snabbare än något av exemplen ovan:
void print ( std :: string_view s ) { std :: cout << s ; } ... std :: sträng x = ...; print ( x ); // kopierar inte x.data() print ( "detta är en bokstavlig sträng" ) ; // kopierar inte heller tecknen! ...
Exempel användning
#include <iostream> #include <iomanip> #include <string> int main () { std :: string foo = "fighters" ; std :: string bar = "pall" ; if ( foo != bar ) std :: cout << "Strängarna är olika! \n " ; std :: cout << "foo = " << std :: citerad ( foo ) << " medan bar = " << std :: citerad ( stapel ); }
Relaterade klasser
std::string är en typedef för en viss instansiering av mallklassen std::basic_string . Dess definition finns i <string> -huvudet:
använder string = std :: basic_string < char > ;
Sträng tillhandahåller därför basic_string- funktionalitet för strängar som har element av typen char . Det finns en liknande klass std::wstring , som består av wchar t , och som oftast används för att lagra UTF-16- text på Windows och UTF-32 på de flesta Unix-liknande plattformar. C++-standarden ålägger dock ingen tolkning som Unicode- kodpunkter eller kodenheter på dessa typer och garanterar inte ens att en wchar_t innehåller fler bitar än en char . För att lösa några av de inkompatibiliteter som härrör från wchar_ts egenskaper lade C++11 till två nya klasser: std::u16string och std::u32string (som består av de nya typerna char16_t och char32_t ), som är det givna antalet bitar per kodenhet på alla plattformar. C++11 lade också till nya strängliteraler med 16-bitars och 32-bitars "tecken" och syntax för att sätta Unicode-kodpunkter i nollterminerade (C-stil) strängar.
En basic_string är garanterat specialiserad för alla typer med en char_traits- struktur som ackompanjerar den. Från och med C++11 krävs endast char , wchar_t , char16_t och char32_t specialiseringar för att implementeras.
En basic_string är också en Standard Library-behållare , och därför kan Standard Library-algoritmerna appliceras på kodenheterna i strängar.
Kritik
Designen av std::string har hållits upp som ett exempel på monolitisk design av Herb Sutter , som anser att av de 103 medlemsfunktionerna på klassen i C++98, 71 kunde ha frikopplats utan förlust av implementeringseffektivitet.