Si ha programado en C, estará acostumbrado a la familia de funciones que leen, escriben, modifican y copian cadenas. Existen dos aspectos poco afortunados en la funciones de la librería estándar de C para manipular cadenas. Primero, hay dos familias pobremente organizadas: el grupo plano, y aquellos que requieren que se les suministre el número de caracteres para ser consideradas en la operación a mano. La lista de funciones en la librería de cadenas de C sorprende al usuario desprevenido con una larga lista de nombres crípticos y mayoritariamente impronunciables. Aunque el tipo y número de argumentos es algo consistente, para usarlas adecuadamente debe estar atento a los detalles de nombres de la función y a los parámetros que le pasas.
La segunda trampa inherente a las herramientas para cadenas del estándar de C es que todas ellas explícitamente confían en la asunción de que cada cadena incluye un terminador nulo. Si por confusión o error el terminador nulo es omitido o sobrescrito, poco se puede hacer para impedir que las funciones de cadena de C manipulen la memoria más allá de los límites del espacio de alojamiento, a veces con resultados desastrosos.
C++ aporta una vasta mejora en cuanto a conveniencia y seguridad
de los objetos string
. Para los
propósitos de las actuales operaciones de manipulación, existe
el mismo número de funciones que la librería de C, pero gracias
a la sobrecarga, la funcionalidad es mucho mayor. Además, con
una nomenclatura más sensata y un acertado uso de los argumentos
por defecto, estas características se combinan para hacer de la
clase string
mucho más fácil de usar que
la biblioteca de funciones de cadena de C.
Uno de los aspectos más valiosos y convenientes de los
string
en C++ es que crecen cuando lo
necesitan, sin intervención por parte del programador. No solo
hace el código de manejo del string
sea
inherentemente mas confiable, además elimina por completo las
tediosas funciones "caseras" para controlar los limites del
almacenamiento en donde nuestra cadena reside. Por ejemplo,
si crea un objeto string
e
inicializa este string
con 50 copias de
"X", y después copia en el 50 copias de "Zowie", el objeto,
por sí mismo, readecua suficiente almacenamiento para
acomodar el crecimiento de los datos. Quizás en ningún otro
lugar es más apreciada esta propiedad que cuando las
cadenas manipuladas por su código cambian de tamaño y no
sabe cuan grande puede ser este cambio. La función miembro
append()
e insert()
de string
reubican de manera
transparente el almacenamiento cuando un
string
crece:
//: C03:StrSize.cpp #include <string> #include <iostream> using namespace std; int main() { string bigNews("I saw Elvis in a UFO. "); cout << bigNews << endl; // How much data have we actually got? cout << "Size = " << bigNews.size() << endl; // How much can we store without reallocating? cout << "Capacity = " << bigNews.capacity() << endl; // Insert this string in bigNews immediately // before bigNews[1]: bigNews.insert(1, " thought I"); cout << bigNews << endl; cout << "Size = " << bigNews.size() << endl; cout << "Capacity = " << bigNews.capacity() << endl; // Make sure that there will be this much space bigNews.reserve(500); // Add this to the end of the string: bigNews.append("I've been working too hard."); cout << bigNews << endl; cout << "Size = " << bigNews.size() << endl; cout << "Capacity = " << bigNews.capacity() << endl; } ///:~
Listado 4.2. C03/StrSize.cpp
Aquí la salida desde un compilador cualquiera:
I saw Elvis in a UFO. Size = 22 Capacity = 31 I thought I saw Elvis in a UFO. Size = 32 Capacity = 47 I thought I saw Elvis in a UFO. I've been working too hard. Size = 59 Capacity = 511
Este ejemplo demuestra que aunque puede ignorar con seguridad
muchas de las responsabilidades de reserva y gestión de la
memoria que tus string
ocupan, C++
provee a los string
con varias
herramientas para monitorizar y gestionar su tamaño. Nótese
la facilidad con la que hemos cambiado el tamaño de la memoria
reservada para los string
. La función
size()
retorna el numero de caracteres
actualmente almacenados en el string
y
es idéntico a la función miembro
lenght()
. La función
capacity()
retorna el tamaño de la
memoria subyacente actual, es decir, el número de caracteres
que el string
puede almacenar sin tener
que reservar más memoria. La función
reserve()
es una optimización del
mecanismo que indica su intención de especificar cierta
cantidad de memoria para un futuro uso;
capacity()
siempre retorna un valor al
menos tan largo como la ultima llamada a
reserve()
. La función
resize()
añade espacio si el nuevo tamaño
es mayor que el tamaño actual del
string
; sino trunca el
string
. (Una sobrecarga de
resize()
puede especificar una adición
diferente de caracteres).
La manera exacta en que las funciones miembro de
string
reservan espacio para sus datos
depende de la implementación de la librería. Cuando testeamos
una implementación con el ejemplo anterior, parece que se
hacia una reserva de una palabra de memoria (esto es, un
entero) dejando un byte en blanco entre cada una de ellas. Los
arquitectos de la clase string se esforzaron para poder
mezclar el uso de las cadenas de caracteres de C y los objetos
string
, por lo que es probable por lo
que se puede observar en StrSize.cpp, en
esta implementación en particular, el byte esté añadido para
acomodar fácilmente la inserción de un terminador nulo.