2009-04-11 15 views
6

Estoy lidiando con muchas cadenas en mi programa. Estos datos de cadena no cambian durante toda la vida después de que se leen en mi programa.Cómo liberar la capacidad no utilizada de una cadena

Pero como la cadena C++ se reserva capacidad, desperdician mucho espacio que no se usará con seguridad. Traté de liberar esos espacios, pero no funcionó.

El siguiente es el código simple que he intentado:

string temp = "123456789"; 
string str; 

cout << str.capacity() << endl; 

str.reserve(16);  
cout << str.capacity() << endl;  
// capacity is 31 on my computer  

str += temp;  
cout << str.capacity() << endl;  

str.reserve(16);  
cout << str.capacity() << endl;  
// can't release. The capacity is still 31. 

(El compilador de Visual C++)

Cómo podría liberarlo?

+0

Tener en realidad te quedas sin memoria? Si no, esta es una optimización prematura. Si se ha quedado sin memoria, ¿qué objetos dominarán realmente su asignación de memoria? El punto es que este tipo de cosas no es útil. –

+0

No hay forma de forzar al asignador a usar menos bytes ** para la construcción inicial de una cadena dada ** - al menos no sin escribir su propia plantilla de clase de asignador STL (y posiblemente ni siquiera entonces). –

Respuesta

12

Cuando llame al reserve, está haciendo una solicitud para cambiar la capacidad. Las implementaciones solo garantizarán que se reserve un número igual o superior a esta cantidad. Por lo tanto, una implementación particular puede ignorar de manera segura una solicitud de reducción de capacidad.

Sin embargo, le recomiendo que considere si esto no es una optimización prematura. ¿Estás seguro de que estás haciendo tantas cosas que es un cuello de botella de memoria para ti? ¿Estás seguro de que en realidad es la memoria el cuello de botella?

De la documentación para reserve:

Esto se puede ampliar o reducir el tamaño de el espacio de almacenamiento en la cadena, aunque aviso de que el capacidad resultante después de llamar a esta función no es necesariamente igual a res_arg pero puede ser igual o superior a que a res_arg, por lo tanto, las solicitudes de reducción pueden o no producir una reducción real del asignado espacio en una biblioteca particular implementación. En cualquier caso, nunca recorta el contenido de la cadena (para ese propósito , vea cambiar el tamaño o borrar, que modifica el contenido).

+0

+1 me parece una optimización prematura. –

5

¿Por qué no usa una matriz de caracteres entonces?

+1

+1. Una matriz char de la biblioteca se puede convertir en std :: string cuando desee utilizarla y destruirla más tarde. – GogaRieger

+0

Suponiendo que se refiere a la asignación dinámica, incluso es probable que "desperdicie" al menos 4 bytes para mantener la longitud, y probablemente el asignador dinámico interno probablemente se asigne en bloques no menores de 16/32 bytes por razones de eficiencia. –

3

Creo que puede usar el método de intercambio para liberar los datos. cámbielo con una cadena local vacía para que cuando la cadena local se salga del alcance, la memoria se libere.

+0

+1. Esta es una buena (aunque no garantizada) forma de devolver la memoria si desea establecer la cadena en otra cosa. Aunque no ayudará a recortar espacio si el asignador subyacente lo redondea a los 16 bytes más cercanos (como parece hacer MSVC++), nada lo ayudará. –

0

No hay una capacidad mínima garantizada para std::string. Puede solicitar la capacidad que desee llamando al reserve, pero una implementación particular solo garantiza establecer la capacidad en una cantidad mayor o igual al tamaño solicitado.

Aquí es una versión modificada de su programa que pone a prueba varios métodos de reducción cadena:

#include <string> 
#include <iostream> 
using namespace ::std; 

template< typename S > 
S & reserve_request(S & s, typename S::size_type n) { 
    s.reserve(n); return s; 
} 

template< typename S > 
S & shrink_request1(S & s) { s.reserve(); return s; } 

template< typename S > 
S & shrink_request2(S & s) { S(s).swap(s); return s; } 

template< typename S > 
S & shrink_request3(S & s) { S(s.c_str()).swap(s); return s; } 

template< typename S > 
void test(S & s) { cout << s.capacity() << endl; } 

int main() { 
    string temp = "123456789"; // length 16 
    string str; 

    test(str);       // 15 
    test(reserve_request(str, 16)); // 31 
    test(str += temp);     // 31 
    test(reserve_request(str, 16)); // 31 
    test(shrink_request1(str));  // 31 
    test(shrink_request2(str));  // 31 
    test(shrink_request3(str));  // 31 
    return 0; 
} 

Parecería que Visual C++ 's std::string normalmente mantiene cierta capacidad de reserva.

Si su proyecto carga grandes cantidades de cadenas leídas desde una fuente externa cuyo tamaño nunca cambia, podría estar mejor (como han sugerido otros) almacenarlas en un solo bloque grande de memoria de caracteres separados por '\0' caracteres (es decir, como C-strings). Si lo desea, puede proporcionar funciones de envoltura que devuelvan el std::string s sobre la marcha.

4

deletreando Naveen's answer:

string x = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; 
cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52 

// This tends not to work (although in theory it could): 
//x = "XYZ"; 
//cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52 

// This tends to work (although in theory it might not): 
string("XYZ").swap(x); 
cerr << x.capacity() << "\n"; // MSVC++: 15 g++: 3 

Tenga en cuenta que si el asignador subyacente asigna más de n bytes cuando se construye un string de longitud n (por ejemplo, mediante redondeo al más cercano 32 como aparece MSVC++ para do), no hay forma de hacer que use menos bytes. Pero probablemente no quiera hacer eso de todos modos, ya que este "redondeo" se hace para hacer que el proceso de asignación de memoria dinámica sea más eficiente y también tiene el efecto secundario de hacer que la concatenación de cadenas cortas sea más rápida en promedio (ya que se necesitan menos reasignaciones que se produzca).

1

Esto es principalmente específico de la implementación. La idea es minimizar las solicitudes de asignación y fragmentación de memoria. Es fácil probar que al duplicar el tamaño existente cada vez que se expande el bloque, se minimizan el recuento de asignación y la fragmentación de la memoria. Por lo tanto, las implementaciones típicas de contenedor STL duplicarán el bloque existente al expandirse.

Una cosa que puede hacer es usar un asignador personalizado que no asignará más de lo necesario, a continuación, construya sus objetos std :: string cuando ya no necesite manipularlos (o cuando termine de manipular, simplemente cambie a un nuevo std :: sting object - esto es básicamente lo que otros han hecho en sus respuestas) y, finalmente, puede usar un asignador de memoria agrupado para minimizar la fragmentación de la memoria, la pérdida de capacidad y mejorar el rendimiento.

Ver:

http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079 http://www.sjbrown.co.uk/2004/05/01/pooled-allocators-for-the-stl/ http://www.codeproject.com/KB/stl/blockallocator.aspx

Búsqueda de "Allocator STL" y "memoria de grupo"

4

Prueba el std::string de intercambio-truco para reducir sus cadenas:

std::string(str.data(), str.size()).swap(str) 

Donde str es la cadena que desea reducir al tamaño.

0

capacidad NUNCA será menor que 15 con dinkumware STL. std :: basic_string tiene una unión que es el puntero a un búfer asignado o un tampón de 16 bytes (si la capacidad() < = 15) (para las cadenas de carbón de leña)

ver el archivo de cabecera xstring

en el Por ejemplo, donde reserva 16, en realidad está reservando 17 (uno para nulo) que es> 16, por lo que se asigna en lugar de estar en caché en el byte de 16 bytes en la unión del puntero de caché. Esa asignación duplica el tamaño anterior (16) por lo que obtienes 32. La capacidad de esa cadena es probablemente 31.

Pero esto depende de la implementación STL.

Cambiar el parámetro de la plantilla del asignador en una plantilla decl de std :: basic_string NO es suficiente - la elección de cuándo asignar y cuánto, en std :: basic_string crece el algoritmo NO en el asignador. La duplicación del tamaño anterior (y la contracción cuando < 1/4) es lo estándar en la Introducción a los algoritmos - Cormen Lieserson Rivest Stein

No está seguro acerca de la reducción en algo Dinkumware .......

Cuestiones relacionadas