2011-07-28 8 views
6

Declaro una variable string s;Cadena de C++ utiliza el búfer máximo asignado?

y do s = "abc"; ahora tiene un buffer de 3 caracteres.

Después

s = "abcd" que tiene un buffer de 4 caracteres.

Ahora, después de la tercera declaración

s = "ab" pregunta es va a mantener el buffer de 4 caracteres o va a reasignar un buffer de 2 carácter?

Si va a asignar memoria intermedia de 2 caracteres, hay alguna forma de que pueda mantener el búfer máximo asignado.

¿Mantiene el búfer de tamaño máximo asignado?

s = "ab" 
s="abc" 
s="a" 
s="abcd" 
s="b" 

Ahora se debe mantener un buffer de tamaño 4.

¿Es posible?

Respuesta

7

s = "ab" La pregunta es ¿mantendrá el búfer de 4 palabras o reasignará el búfer de 2 palabras?

No reasignará el búfer. No sé si se menciona en el estándar, pero todas las implementaciones que he visto emiten una reasignación solo si necesitan aumentar la capacidad. Nunca para disminuir. Incluso si tiene una cadena con 4 caracteres y llama al .resize(2) o .reserve(2), la capacidad no cambiará. Con el fin de obligar a la cadena (o contenedores) para reasignar memoria para ajustarse al tamaño exacto, hay un sencillo truco para que swap

s.swap(string(s)); 

¿Qué pasa aquí? Usted crea un temporal de s que tendrá su capacidad exactamente igual a s.size() y luego lo intercambiará con su cadena original. El destructor del temporal liberará todos los recursos necesarios.

Una vez más, no pretendo que esto sea estándar, pero todas las implementaciones que he visto tienen este comportamiento.

+2

Depende de la implementación: g ++ cambiará la capacidad si le asigna una nueva cadena, con una capacidad más pequeña, y VC++ usualmente usará la capacidad más pequeña que pueda de un conjunto fijo de capacidades. (Sospecho que estás pensando en 'std :: vector'; lo que describes es el comportamiento de ni g ++ ni VC++, y no puedo creer que no hayas visto ninguno de esos). –

10

La cadena mantendrá su búfer una vez asignada, y solo reasignará si necesita un búfer aún mayor. También es probable que comience con un tamaño de búfer inicial superior a 3 o 4.

Puede verificar el tamaño asignado utilizando la función de miembro capacity().


Después de los comentarios de James a continuación, sigo creyendo que mi respuesta es correcta para los ejemplos que figuran en la pregunta.

Sin embargo, para una referencia contado aplicación una secuencia como este

s = "some rather long string..."; 

std::string t = "b"; 
s = t; 

fijaría s.capacity() para ser igual a t.capacity() si la aplicación decide compartir la memoria intermedia interna entre s y t.

+0

entonces después de s = "b" mantendrá el tamaño de 5 ¿verdad? –

+1

Bueno, 'size()' es el número de caracteres en la cadena. Mantendrá su buffer 'capacity()' incluso cuando se le asigne una cadena más pequeña. –

+0

Esto depende mucho de la implementación, y lo que se describe no corresponde ni al comportamiento de VC++ ni a g ++. –

0

Puede ver fácilmente el comportamiento de su implementación llamando al std::string::capacity en varias ocasiones. En general, me sorprendería si alguna implementación tuviera un buffer de tres caracteres. (No palabras, pero bytes, al menos en la mayoría de las máquinas modernas). En la práctica, las implementaciones varían, y también varían dependiendo de cómo se produce la nueva longitud : con g ++, por ejemplo, eliminar caracteres con std::string::erase no reducirá la capacidad de la cadena, pero asignando una cadena nueva y más pequeña lo hará. VC++ no reduce la capacidad en ningún caso. (En general, VC++ y g ++ tienen muy diferentes estrategias con respecto a la gestión de memoria en las cadenas.)

EDIT:

Dadas las otras respuestas (que ni siquiera se corresponden con práctica habitual): aquí está la programa de prueba pequeña he utilizado para verificar mis declaraciones anterior (aunque realmente no lo necesitaba para g ++ — conozco las internos de la aplicación bastante bien):

#include <string> 
#include <iostream> 
#include <iomanip> 

template<typename Traits> 
void 
test() 
{ 
    std::string s; 
    size_t lastSeen = -1; 
    std::cout << Traits::name() << ": Ascending:" << std::endl; 
    while (s.size() < 150) { 
     if (s.capacity() != lastSeen) { 
      std::cout << " " << std::setw(3) << s.size() 
       << ": " << std::setw(3) << s.capacity() << std::endl; 
      lastSeen = s.capacity(); 
     } 
     Traits::grow(s); 
    } 
    std::cout << Traits::name() << ": Descending: " << std::endl; 
    while (s.size() != 0) { 
     Traits::shrink(s); 
     if (s.capacity() != lastSeen) { 
      std::cout << " " << std::setw(3) << s.size() 
       << ": " << std::setw(3) << s.capacity() << std::endl; 
      lastSeen = s.capacity(); 
     } 
    } 
    std::cout << "Final: capacity = " << s.capacity() << std::endl; 
} 

struct Append 
{ 
    static void grow(std::string& s) 
    { 
     s += 'x'; 
    } 
    static void shrink(std::string& s) 
    { 
     s.erase(s.end() - 1); 
    } 
    static std::string name() 
    { 
     return "Append"; 
    } 
}; 

struct Assign 
{ 
    static void grow(std::string& s) 
    { 
     s = std::string(s.size() + 1, 'x'); 
    } 
    static void shrink(std::string& s) 
    { 
     s = std::string(s.size() - 1, 'x'); 
    } 
    static std::string name() 
    { 
     return "Assign"; 
    } 
}; 

int 
main() 
{ 
    test<Append>(); 
    test<Assign>(); 
    return 0; 
} 

intentarlo. Los resultados son bastante instructivos.

Cuestiones relacionadas