2012-03-01 17 views
15

Quiero usar std :: string simplemente para crear un buffer dinámico y para iterar a través de él usando un índice. ¿Resize() la única función para asignar realmente el buffer?std :: strings's capacity(), reserve() & resize() functions

Intenté reservar() pero cuando intento acceder a la cadena a través del índice, afirma. También cuando la capacidad predeterminada de la cadena parece ser de 15 bytes (en mi caso), pero si sigo sin poder acceder a ella como my_string[1].

¿Entonces la capacidad de la cadena no es la memoria intermedia real? También reserve() tampoco asigna el buffer real?

string my_string; 

// I want my string to have 20 bytes long buffer 
my_string.reserve(20); 

int i = 0; 

for (parsing_something_else_loop) 
{ 
    char ch = <business_logic>; 

    // store the character in 
    my_string[i++] = ch; // this crashes 
} 

Si hago el cambio de tamaño() en lugar de reservar() que funciona bien. ¿Cómo es que la cuerda tiene la capacidad pero no puede acceder a ella con []? ¿No es ese el tamaño de reservar() para que puedas acceder a él?

Add-on En respuesta a las respuestas, me gustaría pedir a la gente stl, ¿por qué nadie reserva de uso() cuando cambio de tamaño() hace exactamente lo mismo y también iniciar la cadena? Debo decir que no aprecio tanto el argumento del rendimiento en este caso. Todo lo que resize() hace además de lo que reserva() es que simplemente inicializa el búfer que sabemos que siempre es bueno hacer de todos modos. ¿Podemos votar reservar() fuera de la isla?

+2

¿Sería un vector de bytes más apropiado – rerun

+0

** ** std :: vector parece también una posible solución para sus necesidades . ¿Una respuesta con std :: vector también sería una respuesta aceptable? –

Respuesta

25

¿No es ese el tamaño de la reserva() para que pueda acceder?

No, ese es el punto de resize().

reserve() solo da suficiente espacio para que las futuras llamadas que aumenten el tamaño (por ejemplo, llamar al push_back()) sean más eficientes.

En su caso de uso, parece que debería usar .push_back() en su lugar.

my_string.reserve(20); 

for (parsing_something_else_loop) 
{ 
    char ch = <business_logic>; 
    my_string.push_back(ch); 
} 

¿Cómo es que la cadena tiene la capacidad, pero en realidad no puede acceder a ella con []?

Llamar .reserve() es como volar montañas para darle un poco de tierra libre. La cantidad de tierra libre es .capacity(). La tierra está allí, pero eso no significa que puedas vivir allí. Tienes que construir casas para mudarte. El número de casas es el .size() (= .length()).

Supongamos que está construyendo una ciudad, pero después de construir la 50.a encontró que no hay suficiente tierra, por lo que necesita encontrar otro lugar lo suficientemente grande para caber en la casa 51 y luego migrar a toda la población allí. Esto es extremadamente ineficiente. Si usted supiera que necesita para construir 1000 casas por adelantado, entonces se puede llamar

my_string.reserve(1000); 

obtener suficiente terreno para construir 1.000 casas, y luego llamar a

my_string.push_back(ch); 

para la construcción de la casa con la asignación de ch a esta ubicación. La capacidad es 1000, pero el tamaño sigue siendo 1.No puede decir

my_string[16] = 'c'; 

porque la casa # 16 todavía no existe. Puede llamar

my_string.resize(20); 

para obtener casas # 0 ~ # 19 construidos de una sola vez, por lo que

my_string[i++] = ch; 

funciona bien (siempre y cuando 0 ≤ i ≤ 19).

Véase también http://en.wikipedia.org/wiki/Dynamic_array.


Por su pregunta complemento,

.resize() no puede reemplazar completamente .reserve(), debido a que (1) no siempre es necesario utilizar todos los espacios asignados, y (2) la construcción por defecto + de asignación de copia es un proceso de dos pasos, lo que podría tomar más tiempo que la construcción directa (esp. para objetos grandes), es decir

#include <vector> 
#include <unistd.h> 

struct SlowObject 
{ 
    SlowObject() { sleep(1); } 
    SlowObject(const SlowObject& other) { sleep(1); } 
    SlowObject& operator=(const SlowObject& other) { sleep(1); return *this; } 
}; 

int main() 
{ 
    std::vector<SlowObject> my_vector; 

    my_vector.resize(3); 
    for (int i = 0; i < 3; ++ i) 
     my_vector[i] = SlowObject(); 

    return 0; 
} 

va a desperdiciar al menos 9 segundos para correr, mientras

int main() 
{ 
    std::vector<SlowObject> my_vector; 

    my_vector.reserve(3); 
    for (int i = 0; i < 3; ++ i) 
     my_vector.push_back(SlowObject()); 

    return 0; 
} 

desperdicia solo 6 segundos.

std::string solo copia la interfaz de std::vector aquí.

+1

Piénselo de esta manera: la cadena todavía tiene un tamaño que no ha sido modificado por 'reserve()'; sin embargo, ha asignado un fragmento contiguo de memoria para que la cadena pueda ser 'resize()' 'd sin incurrir en una copia de toda la cadena. Sin embargo, seguirá verificando que su índice esté dentro del límite del tamaño (lógico) de la cadena. Reservar es una optimización cuando tienes conocimiento anticipado sobre cuán grande es la cadena, es como el c idioma de char [1000]; tienes 1000 bytes disponibles aunque la cadena solo sea "Hola, mundo" – Matt

+0

Entonces digamos que empiezo con su ejemplo con 'my_string.resize (1000)', ¿cuál es el rendimiento realmente? Argumentaré que es mejor porque reduce una línea en el código (el cambio de tamaño() más adelante) por lo que tengo menos código para administrar. – zar

+0

@zadane: Ver actualización. – kennytm

3

El capacity es la longitud del búfer actual, sino que el buffer es privada a la cadena; en otras palabras, no es suyo para acceder. El std::string de la biblioteca estándar puede asignar más memoria de la necesaria para almacenar los caracteres reales de la cadena. La capacidad es la longitud total asignada. Sin embargo, el acceso a caracteres fuera de s.begin() y s.end() sigue siendo ilegal.

Llama al reserve en los casos en que prevé cambiar el tamaño de la cadena para evitar reasignaciones innecesarias. Por ejemplo, si planea concatenar diez cadenas de 20 caracteres en un bucle, puede tener sentido reservar 201 caracteres (uno adicional para el terminador cero) para su cadena, en lugar de expandirlo varias veces desde su tamaño predeterminado .

4

No - el objetivo de reserve es evitar la reasignación. resize establece el tamaño utilizable, reserve no; simplemente establece una cantidad de espacio que está reservado, pero que aún no se puede usar directamente.

Aquí hay un ejemplo - que vamos a crear una cadena aleatoria de 1000 caracteres:

static const int size = 1000; 
std::string x; 
x.reserve(size); 
for (int i=0; i<size; i++) 
    x.push_back((char)rand()); 

reserve es principalmente una herramienta de optimización sin embargo - la mayoría del código que funciona con reserve también debería funcionar (solo, posiblemente, un poco más lentamente) sin llamar al reserve. La única excepción es que reserve puede garantizar que los iteradores permanezcan válidos, cuando no lo harían sin la llamada de reserva.

2

reserve(n) de hecho asigna almacenamiento suficiente para contener al menos n elementos, pero en realidad no llena el contenedor con ningún elemento. La cadena todavía está vacía (tiene tamaño 0), pero está garantizado que puede agregar (por ejemplo, push_back o insert) al menos n elementos antes de que se necesite reasignar el búfer interno de la cadena, mientras que resize(n) realmente cambia el tamaño de la cadena para contener n elementos (y elimina o agrega nuevos elementos si es necesario).

Así reserve es en realidad una mera instalación de optimización, cuando se sabe que va a agregar un montón de elementos para el recipiente (por ejemplo, en un bucle push_back) y no desea que reasignar el almacenamiento con demasiada frecuencia, lo que incurre en la asignación de memoria y copiando costos. Pero no cambia la vista de afuera/cliente de la cadena. Todavía permanece vacío (o mantiene su conteo actual de elementos).

Asimismo capacity devuelve el número de elementos de la cadena puede contener hasta que se necesita para reasignar su almacenamiento interno, mientras que size (y para cadena también length) devuelve el número real de elementos en la cadena.

-1

std :: vector en lugar de std :: string también podría ser una solución - si no hay requisitos en contra de ella.

vector<char> v; // empty vector 
vector<char> v(10); // vector with space for 10 elements, here char's 

tu ejemplo:

vector<char> my_string(20); 

int i=0; 

for (parsing_something_else_loop) 
{ 
    char ch = <business_logic>; 
    my_string[i++] = ch; 
} 
+0

Esto realmente no responde a la pregunta real del OP, sino que simplemente lo transforma en un problema completamente idéntico. –

+0

Le pregunté en un comentario debajo de la pregunta si std :: vector podría ser una opción, no vi sí o no y le ofrecí una solución alternativa, incluso marcada, como alternativa ("std :: vector en vez de std :: string might también sea una solución "). No veo el daño que ves. –

+0

No veo ningún daño, también, siempre y cuando los comentarios se queden comentarios y las respuestas se mantengan. Pero su "respuesta" es solo un comentario, aunque es bueno, pero sigue siendo un comentario y no una respuesta a ninguna de sus preguntas. –

1

El hecho de que reserve asigna espacio adicional, no significa que es legítimo para acceder a él.

En su ejemplo, o bien utilizar resize, o volver a escribir a algo como esto:

string my_string; 

// I want my string to have 20 bytes long buffer 
my_string.reserve(20); 

int i = 0; 

for (parsing_something_else_loop) 
{ 
    char ch = <business_logic>; 

    // store the character in 
    my_string += ch; 
}