Tabla de contenidos
El procesamiento de cadenas de caracteres en C es una de las
mayores pérdidas de tiempo. Las cadenas de caracteres requieren
que el programador tenga en cuenta las diferencias entre cadenas
estáticas y las cadenas creadas en la pila y en el montón,
además del hecho que a veces pasa como argumento un
char*
y a veces hay que copiar el arreglo entero.
Precisamente porque la manipulación de cadenas es muy común, las
cadenas de caracteres son una gran fuente de confusiones y
errores. Es por ello que la creación de clases de cadenas sigue
siendo desde hace años un ejercicio común para programadores
novatos. La clase string
de la biblioteca
estándar de C++ resuelve el problema de la manipulación de
caracteres de una vez por todas, gestionando la memoria incluso
durante las asignaciones y las construcciones de
copia. Simplemente no tiene que preocuparse por ello.
Este capítulo[1] examina la clase
string
del Estándar C++; empieza con un
vistazo a la composición de las string
de
C++ y como la versión de C++ difiere del tradicional arreglo de
caracteres de C. Aprenderá sobre las operaciones y la manipulación
usando objetos string
, y verá como éstas se
FIXME[acomodan a la variación] de conjuntos de caracteres y
conversión de datos.
Manipular texto es una de las aplicaciones más antiguas de la
programación, por eso no resulta sorprendente que las
string
de C++ estén fuertemente inspiradas
en las ideas y la terminología que ha usado continuamente en C y
otros lenguajes. Conforme vaya aprendiendo sobre los
string
de C++, este hecho se debería ir
viendo más claramente. Da igual el lenguaje de programación que
escoja, hay tres cosas comunes que querrá hacer con las cadenas:
Crear o modificar secuencias de caracteres almacenados en una cadena
Detectar la presencia o ausencia de elementos dentro de la cadena
Traducir entre diversos esquemas para representar cadenas de caracteres
Verá como cada una de estas tareas se resuelve usando objetos
string
en C++.
En C, una cadena es simplemente un arreglo de caracteres que
siempre incluye un 0 binario (frecuentemente llamado terminador
nulo) como elemento final del arreglo. Existen diferencias
significativas entre los string
de C++ y
sus progenitoras en C. Primero, y más importante, los
string
de C++ esconden la implementación
física de la secuencia de caracteres que contiene. No debe
preocuparse de las dimensiones del arreglo o del terminador
nulo. Un string
también contiene cierta
información para uso interno sobre el tamaño y la localización
en memoria de los datos. Específicamente, un objeto
string
de C++ conoce su localización en
memoria, su contenido, su longitud en caracteres, y la cantidad
de caracteres que puede crecer antes de que el objeto
string
deba redimensionar su buffer
interno de datos. Las string
de C++, por
tanto, reducen enormemente las probabilidades de cometer uno de
los tres errores de programación en C más comunes y
destructivos: sobrescribir los límites del arreglo, intentar
acceder a un arreglo no inicializado o con valores de puntero
incorrectos, y dejar punteros colgando después de que el arreglo
deje de ocupar el espacio que estaba ocupando.
La implementación exacta del esquema en memoria para una clase
string no esta definida en el estándar C++. Esta arquitectura
esta pensada para ser suficientemente flexible para permitir
diferentes implementaciones de los fabricantes de compiladores,
garantizando igualmente un comportamiento predecible por los
usuarios. En particular, las condiciones exactas de cómo situar
el almacenamiento para alojar los datos para un objeto
string
no están definidas. FIXME: Las
reglas de alojamiento de un string
fueron
formuladas para permitir, pero no requerir, una implementación
con referencias múltiples, pero dependiendo de la implementación
usar referencias múltiples sin variar la semántica. Por decirlo
de otra manera, en C, todos los arreglos de char ocupan una
única región física de memoria. En C++, los objetos
string
individuales pueden o no ocupar
regiones físicas únicas de memoria, pero si su conjunto de
referencias evita almacenar copias duplicadas de datos, los
objetos individuales deben parecer y actuar como si tuvieran sus
propias regiones únicas de almacenamiento.
//: C03:StringStorage.h #ifndef STRINGSTORAGE_H #define STRINGSTORAGE_H #include <iostream> #include <string> #include "../TestSuite/Test.h" using std::cout; using std::endl; using std::string; class StringStorageTest : public TestSuite::Test { public: void run() { string s1("12345"); // This may copy the first to the second or // use reference counting to simulate a copy: string s2 = s1; test_(s1 == s2); // Either way, this statement must ONLY modify s1: s1[0] = '6'; cout << "s1 = " << s1 << endl; // 62345 cout << "s2 = " << s2 << endl; // 12345 test_(s1 != s2); } }; #endif // STRINGSTORAGE_H ///:~
Listado 4.1. C03/StringStorage.h
Decimos que cuando una implementación solo hace una sola copia
al modificar el string
usa una
estrategia de copiar al escribir. Esta aproximación ahorra
tiempo y espacio cuando usamos string
como parámetros por valor o en otras situaciones de solo
lectura.
El uso de referencias múltiples en la implementación de una librería debería ser transparente al usuario de la clase string. Desgraciadamente, esto no es siempre el caso. En programas multihilo, es prácticamente imposible usar implementaciones con múltiples referencias de forma segura[32].[2]