El lenguaje de programación C++ admite el manejo de cadenas , implementado principalmente en su biblioteca estándar . El estándar del lenguaje especifica varios tipos de cadena, algunos heredados de C y otros diseñados para aprovechar las características del lenguaje, como las clases y RAII . El más utilizado es std::string, mientras que std::string_viewse usa para vistas de cadena que no son propietarias.
Dado que las versiones iniciales de C++ solo contaban con la funcionalidad y las convenciones de manejo de cadenas de "bajo nivel" de C , a lo largo de los años se han diseñado múltiples diseños incompatibles para clases de manejo de cadenas que aún se utilizan en lugar de std::string, y los programadores de C++ pueden necesitar manejar múltiples convenciones en una sola aplicación.
Historia
El std::stringtipo es el tipo de dato de cadena principal en C++ estándar desde 1998, pero no siempre formó parte de C++. De C, C++ heredó la convención de usar cadenas terminadas en nulo que se manejan mediante un puntero a su primer elemento, y una biblioteca de funciones que manipulan dichas cadenas. En el C++ estándar moderno, un literal de cadena como "hello"todavía denota una matriz de caracteres terminada en NUL. [ 1 ]
El uso de clases de C++ para implementar un tipo de cadena ofrece varias ventajas de gestión automatizada de memoria y un riesgo reducido de accesos fuera de límites, [ 2 ] y una sintaxis más intuitiva para la comparación y concatenación de cadenas. Por lo tanto, era muy tentador crear una clase de este tipo. A lo largo de los años, los desarrolladores de aplicaciones, bibliotecas y frameworks de C++ produjeron sus propias representaciones de cadena incompatibles, como la de la biblioteca Standard Components de AT&T (la primera implementación de este tipo, 1983) [ 3 ] o el tipo en MFCCString de Microsoft [ 4 ] o en Qt . [ 5 ] [ 6 ]QString
Los distintos tipos de cadenas de caracteres de los proveedores tienen diferentes estrategias de implementación y características de rendimiento, lo que dificulta la conversión de código e incluso la asignación de uno a otro:
- Algunos utilizaron una estrategia de copia en escritura con importantes implicaciones en el rendimiento (haciendo que algunas operaciones fueran mucho más rápidas y otras mucho más lentas).
- Aunque la mayoría coincidía en la sintaxis para la comparación, la asignación y la concatenación, la sintaxis para extraer subcadenas o realizar búsquedas variaba ampliamente.
- No todo era Unicode, lo que dio lugar a variaciones generalizadas en la forma en que se almacenaba la codificación y en el momento de la traducción, y a veces a operadores de asignación muy costosos. También se creía que se necesitaban unidades de código mayores que los bytes para admitir Unicode, lo que dio lugar a tipos de cadena que no podían interoperar de manera eficiente. [ 7 ]
Aunque std::stringlas cadenas están estandarizadas, las aplicaciones heredadas todavía suelen contener tipos de cadena personalizados y las bibliotecas pueden esperar cadenas al estilo C, lo que hace "prácticamente imposible" evitar el uso de múltiples tipos de cadena en programas C++ [ 1 ] y obliga a los programadores a decidir la representación de cadena deseada antes de comenzar un proyecto. [ 4 ] En una retrospectiva de 1991 sobre la historia de C++, su inventor Bjarne Stroustrup calificó la falta de un tipo de cadena estándar (y algunos otros tipos estándar) en C++ 1.0 como el peor error que cometió en su desarrollo; "la ausencia de estos llevó a que todos reinventaran la rueda y a una diversidad innecesaria en las clases más fundamentales". [ 3 ]
Descripción
La std::stringclase es la representación estándar para una cadena de texto desde C++98 . La clase proporciona algunas operaciones típicas de cadena como comparación, concatenación, búsqueda y reemplazo, y una función para obtener subcadenas . Se std::stringpuede construir una cadena a partir de una cadena de estilo C, y también se puede obtener una cadena de estilo C a partir de una. [ 8 ] Las unidades individuales que componen la cadena son de tipo char . En el uso moderno, a menudo no son "caracteres", sino partes de una codificación de caracteres multibyte como UTF-8 .
importar std ;usando std :: string ;int main () { string foo = "fighters" ; string bar = "stool" ; if ( foo != bar ) { std :: println ( "¡Las cadenas son diferentes!" ); } foo += bar + " end" ; // foo ahora es "fightersstool end" std :: println ( "{}" , foo ); // imprimir }Se puede construir una cadena a partir de un literal de cadena utilizando el operador: por ejemplo, [ 9 ]std::literals::string_literals::operator""sstrings="Hello, world!"s;
Copiar en Escribir
Una implementación de copia en escritura significa
cadena a = "¡hola!" ; cadena b = a ; // Constructor de copiaEn realidad, no copia el contenido de aa b; en cambio, ambas cadenas comparten su contenido y se incrementa un contador de referencias sobre el contenido. La copia propiamente dicha se pospone hasta que una operación de modificación, como cambiar un carácter, hace que el contenido de las cadenas sea diferente.
La estrategia de copia en escritura fue permitida deliberadamente por el estándar inicial de C++ porque std::stringse consideró una optimización útil y fue utilizada por casi todas las implementaciones. [ 8 ] Sin embargo, hubo errores, en particular que operator[]devolvía una referencia no constante para facilitar la portabilidad de código C como s[5] = 'a', y tenía que activar una copia. El multihilo provocó que las optimizaciones (como no copiar cuando el contador de referencias es uno) fallaran [ 10 ] y en los procesadores modernos el bloqueo necesario para examinar o cambiar el contador de referencias es más costoso que copiar una cadena pequeña. [ 11 ]
Esto provocó que las implementaciones, primero MSVC y luego GCC, se alejaran de la copia en escritura. [ 12 ] La optimización finalmente se prohibió en C++11 . [ 13 ]
La mayoría de las implementaciones ahora utilizan la Optimización de Cadenas Cortas (SSO), donde las cadenas de menos de 16 (o 23) bytes se almacenan en el objeto de cadena y no se asigna ningún búfer. [ 14 ] Esto es significativamente más rápido que el conteo de referencias, ya que una proporción sorprendente de cadenas son así de cortas, y la asignación de búfer es extremadamente costosa en comparación con la copia de bloques de memoria locales.
Vistas de cadena
A diferencia de otros lenguajes, como Javajava.lang.String o C#System.String , las cadenas de C++ siempre son mutables, ya que las constantes de cadena entre comillas terminadas en nulo cumplían la mayor parte de la función de una clase no mutable. C++ requiere que el código siguiente cree una copia de la cadena, lo cual es bastante lento en comparación con pasar un puntero, incluso si se utilizan contadores de referencias de copia en escritura. [ 11 ]
importar std ;usando std :: string ;void outputString ( string s ) { std :: print ( "{}" , s ); }// ... cadena s = "..." ; outputString ( s ); // hace una copia de s outputString ( "esta es una cadena literal" ); // copia la cadena, a veces dos vecesAunque el compilador podría optimizar esto para funciones en línea, pocos dependían de esto y casi siempre las cadenas se pasan como una referencia constante : [ 15 ] La conversión desde una constante de cadena también requería la construcción de una temporal std::stringy era lenta, lo que generalmente llevaba a funciones sobrecargadas:
importar std ;usando std :: string ;void outputString ( const string & s ) { std :: print ( "{}" , s ); }void outputString ( const char * s ) { std :: print ( "{}" , s ); }// ... cadena s = "..." ; outputString ( s ); // no copia s, pasa un puntero/referencia outputString ( "esta es una cadena literal" ); // llama a la sobrecarga y pasa un puntero sin procesarEl estándar C++17 agrega la std::string_viewclase [ 16 ] que es solo un puntero y una longitud a datos de solo lectura, y es un reemplazo directo para un temporalstd::string inmutable y hace que los argumentos pasados por valor sean más rápidos que cualquiera de los ejemplos anteriores:
importar std ;using std :: string ; using std :: string_view ;void outputString ( string_view sv ) { std :: print ( "{}" , s ); }// ... cadena s = "..." ; outputString ( s ); // un nivel menos de indirección outputString ( "esta es una cadena literal" ); // además, el compilador puede optimizar eliminando la necesidad de usar strlen()Todas las funciones de una biblioteca que toman un argumento de cadena deben reescribirse para aprovechar esto en C++17, ya que la conversión de std::string_viewa std::stringsigue siendo costosa.
Se puede construir una vista de cadena a partir de un literal de cadena utilizando el operador: por ejemplo, [ 17 ]std::literals::string_view_literals::operator""svstring_viewsv="Hello, world!"sv;
Otras unidades de código
std::stringes un typedef para una instancia particular de la std::basic_string<CharT, Traits, Alloc>clase plantilla . [ 18 ] Su definición se encuentra en el encabezado <string> :
espacio de nombres std { usando cadena = cadena_básica < carácter > ; }Hay una clase similar std::wstring, que consta de wchar_t , y se usa con mayor frecuencia para almacenar texto UTF-16 en Windows y UTF-32 en la mayoría de las plataformas tipo Unix . Sin embargo, el estándar C++ no impone ninguna interpretación como puntos de código Unicode o unidades de código en estos tipos y ni siquiera garantiza que un contenga más bits que un . [ 19 ] Para resolver algunas de las incompatibilidades resultantes de las propiedades de , C++11 agregó dos nuevas clases: y (compuestas por los nuevos tipos y ), que son el número dado de bits por unidad de código en todas las plataformas. [ 20 ] C++11 también agregó nuevos literales de cadena de "caracteres" de 16 bits y 32 bits y sintaxis para colocar puntos de código Unicode en cadenas terminadas en nulo (estilo C). [ 21 ]wchar_tcharwchar_tstd::u16stringstd::u32stringchar16_tchar32_t
Se garantiza que A std::basic_string<CharT, Traits, Alloc>es especializable para cualquier tipo con una std::char_traits<CharT>estructura que lo acompañe. A partir de C++11, solo se requiere implementar las especializaciones char, wchar_t, char16_ty . [ 22 ] En , los dos parámetros de plantilla y tienen los valores predeterminados de y por defecto.char32_tstd::basic_string<CharT, Traits, Alloc>TraitsAllocstd::char_traits<CharT>std::allocator<CharT>
A std::basic_stringtambién es un contenedor de la biblioteca estándar y, por lo tanto, los algoritmos de la biblioteca estándar se pueden aplicar a las unidades de código en las cadenas.
Críticas
El diseño std::stringha sido presentado como un ejemplo de diseño monolítico por Herb Sutter , quien considera que de las 103 funciones miembro de la clase en C++98, 71 podrían haberse desacoplado sin pérdida de eficiencia de implementación. [ 23 ]
Véase también
Referencias
- 1 2 Seacord, Robert C. (2013). Codificación segura en C y C++ . Addison-Wesley. ISBN 9780132981972.
- ↑ Oualline, Steve (2003). Programación práctica en C++ . O'Reilly.
- 1 2 Stroustrup, Bjarne (1993). Una historia de C++: 1979–1991 (PDF) . Actas de la Conferencia ACM sobre la historia de los lenguajes de programación.
- 1 2 Solter, Nicholas A.; Kleper, Scott J. (2005). Professional C++ . John Wiley & Sons. p. 23. ISBN 9780764589492.
- ↑ Clase QString
- ↑ Blanchette, Jasmin; Summerfield, Mark (2008). Programación de interfaces gráficas de usuario en C++ con Qt4 . Pearson Education. ISBN 9780132703000.
- ↑ "Clase wstring_convert" . docs.microsoft.com . 3 de agosto de 2021. Consultado el 26 de diciembre de 2021 .
- 1 2 Meyers, Scott (2012), Effective STL , Addison-Wesley, pp. 64– 65, ISBN 9780132979184
- ↑ cppreference.com. "std::literals::string_literals::operator""s" . cppreference.com . cppreference.com . Consultado el 19 de junio de 2026 .
- ↑ "21334 – Falta de seguridad de subprocesos compatible con Posix en STD::basic_string" .
- 1 2 Sutter, Herb (1999). "Optimizaciones que no existen (en un mundo multihilo)" . C/C++ Users Journal . 17 (6).
- ↑ selfboot (17 de enero de 2024). "Modificación inesperada de cadena C++ causada por COW (Copy-On-Write)" . selfboot.cn . Consultado el 13 de mayo de 2025 .
- ↑ Meredith, Alisdair; Boehm, Hans; Crowl, Lawrence; Dimov, Peter (2008). "Modificaciones de concurrencia a la cadena básica" . ISO/IEC JTC 1/SC 22 /WG 21. Recuperado el 19 de noviembre de 2015 .
- ↑ "Dentro de STL: La cadena - Lo viejo y lo nuevo" . Consultado el 17 de junio de 2026 .
- ↑ Stroustrup, Bjarne; Sutter, Herb, eds. (8 de mayo de 2025). "Directrices básicas de C++" . Directrices básicas de Cpp . Consultado el 13 de mayo de 2025 .
- ↑ "std::basic_string_view – cppreference.com" . en.cppreference.com . Consultado el 23 de junio de 2016 .
- ↑ cppreference.com. "std::literals::string_view_literals::operator""sv" . cppreference.com . cppreference.com . Consultado el 19 de junio de 2026 .
- ↑ "Referencia de C++ para basic_string" . Cppreference.com . Consultado el 11 de enero de 2011 .
- ↑ Gillam, Richard (2003). Unicode Demystified: A Practical Programmer's Guide to the Encoding Standard . Addison-Wesley Professional. p. 714. ISBN 9780201700527.
- ↑ "C++11 Paper N3336" . Estándares abiertos . Lenguaje de programación C++, Grupo de trabajo de bibliotecas. 13 de enero de 2012. Consultado el 2 de noviembre de 2013 .
- ↑ Stroustrup, Bjarne (2013). El lenguaje de programación C++ . Addison Wesley. pág. 179. Archivado del original el 25 de noviembre de 2015. Recuperado el 24 de noviembre de 2015 .
- ↑ "char_traits – Referencia de C++" . Consultado el 1 de agosto de 2015 .
- ↑ Sutter, Herb. "Monolitos "Desencadenados"" . gotw.ca . Consultado el 23 de noviembre de 2015 .
- C++
- Biblioteca estándar de C++
- Cadenas de caracteres (informática)