Pregunta

He siguientes preguntas con respecto a las cadenas en C ++

1 >>, que es una mejor opción (teniendo en cuenta el rendimiento) y por qué?

1.

string a;
a = "hello!";

o

2.

string *a;
a = new string("hello!");
...
delete(a);

2 >>

string a;
a = "less"; 
a = "moreeeeeee"; 

¿cómo es exactamente la gestión de memoria se maneja en C ++ cuando una cadena grande se copia en una cadena más pequeña? Son c ++ cadenas mutable?

¿Fue útil?

Solución

Todo lo siguiente es lo que un compilador ingenua haría. Por supuesto, siempre que no cambia el comportamiento del programa, el compilador es libre de hacer cualquier optimización.

string a;
a = "hello!";

En primer lugar se inicializa un para contener la cadena vacía. (Ajustar la longitud a 0, y uno o dos otras operaciones). A continuación, se asigna un nuevo valor, sobrescribir el valor de longitud que ya estaba establecido. También puede tener que realizar una comprobación para ver qué tan grande es el búfer en uso, y si es o no más memoria debe asignarse.

string *a;
a = new string("hello!");
...
delete(a);

Llamada nueva requiere el sistema operativo y el asignador de memoria para encontrar un trozo de memoria libre. Eso es lento. A continuación se inicializa de inmediato, por lo que no se asigna nada dos veces o requiere el buffer para cambiar de tamaño, como se hace en la primera versión. Entonces sucede algo malo, y te olvides de llamar a borrar, y tiene una pérdida de memoria, además a una cadena que es extremadamente lento para asignar. Así que esto es malo.

string a;
a = "less"; 
a = "moreeeeeee";

Al igual que en el primer caso, primero inicializar una para contener la cadena vacía. A continuación, se asigna una nueva cadena, y luego otro. Cada uno de estos pueden requerir una llamada a la nueva asignar más memoria. Cada línea requiere también la longitud, y posiblemente otras variables internas para ser asignada.

Normalmente, se había asignar esta manera:

string a = "hello";

Una línea, realizar la inicialización de una vez, en lugar de primera default-inicialización, y luego asignar el valor que desee.

También minimiza los errores, ya que no tiene una cadena vacía sin sentido en cualquier parte de su programa. Si existe la cadena, que contiene el valor que desea.

Sobre la gestión de memoria, Google RAII. En resumen, la cadena llama new / delete internamente para cambiar el tamaño de su memoria intermedia. Eso significa que no necesitan destinar una cadena con uno nuevo. El objeto de cadena tiene un tamaño fijo, y está diseñado para ser asignado a la pila, de manera que el destructor es automáticamente llama cuando se sale del ámbito. El destructor garantiza entonces que cualquier memoria asignada se libera. De esa manera, no tiene que utilizar la nueva / borrar su código de usuario, lo que significa que no va a perder memoria.

Otros consejos

Casi nunca es necesario o deseable que decir

string * s = new string("hello");

Después de todo, lo haría (casi) nunca dicen:

int * i = new int(42);

En su lugar debe decir

string s( "hello" );

o

string s = "hello";

Y sí, C ++ cadenas son mutables.

¿Hay una razón específica por la cual siempre utiliza una asignación en lugar de inicialización? Es decir, ¿por qué no escribe

string a = "Hello";

etc.? Esto evita una construcción defecto y tiene más sentido semántico. La creación de un puntero a una cadena por el simple hecho de su asignación en el montón no es significativa, es decir, su caso 2 no tiene sentido y es ligeramente menos eficiente.

En cuanto a su última pregunta, sí, cadenas en C ++ son mutables menos const declarado.

string a;
a = "hello!";

2 operaciones: llama al constructor por defecto std: string () y luego llama al operador :: =

string *a; a = new string("hello!"); ... delete(a);

una sola operación: llama al std constructor:. Cadena (const char *) pero no se debe olvidar para liberar el puntero

¿Qué hay de      cadena A ( "Hola");

En el caso 1.1, la cadena miembros (que incluyen puntero a los datos) se llevan a cabo en stack y la memoria ocupada por la instancia de clase se libera cuando a sale del ámbito.

En el caso de 1,2, la memoria para los miembros se asigna dinámicamente del montón también.

Cuando se asigna una constante char* en una cadena, la memoria que contendrá los datos serán realloc'ed para adaptarse a los nuevos datos.

Usted puede ver la cantidad de memoria asignada llamando string::capacity().

Cuando se llama a string a("hello"), la memoria se asigna en el constructor.

Ambos constructor y operador de asignación de llamadas mismos métodos internamente a la memoria asignada y copia los nuevos datos allí.

Si nos fijamos en la docs para la clase string STL (I creer que la documentación de SGI son compatibles con la especificación), muchos de los métodos de la lista garantías complejidad. Creo que muchas de las garantías de complejidad se ha dejado intencionadamente vaga para permitir diferentes implementaciones. Creo que algunas implementaciones de hecho utilizan un método de copia al modificar de tal manera que la asignación de una cadena a otra es una operación constante en el tiempo, pero puede implicar un cargo inesperado cuando intenta modificar uno de esos casos. No estoy seguro si eso sigue siendo cierto en STL moderna sin embargo.

También debe comprobar fuera de la función capacity(), que le dirá la cadena de longitud máxima que puede poner en una instancia determinada cadena antes de que se verá obligado a reasignar memoria. También puede utilizar reserve() para causar una reasignación a una cantidad específica si usted sabe que va a almacenar una gran cadena en la variable en un momento posterior.

Como han dicho otros, por lo que sus ejemplos van, realmente debería favorecer la inicialización sobre otros enfoques para evitar la creación de objetos temporales.

La creación de una cadena directamente en el montón por lo general no es una buena idea, al igual que la creación de tipos base. No vale la pena ya que el objeto puede permanecer fácilmente en la pila y tiene todos los constructores de copia y operador de asignación necesario para una copia eficiente.

El std. Propia cadena tiene una memoria intermedia en el montón que puede ser compartida por varios cadena dependiendo de la aplicación

Para intsance, la aplicación STL de Microsoft que podría hacer lo siguiente:

string a = "Hello!";
string b = a;

Y los dos cuerdas compartiría el mismo tampón hasta que la ha cambiado:

a = "Something else!";

Es por eso que era muy malo para almacenar el c_str () para este último uso; c_str () garentee única validez hasta que otra llamada a la que se hace objeto de cadena.

Esto conduce a errores de concurrencia muy desagradables que requieren esta funcionalidad para compartir que se desactive con un definir si se les utiliza en una aplicación multiproceso

Lo más probable

   string a("hello!");

es más rápido que cualquier otra cosa.

Usted está viniendo desde Java, ¿verdad? En C ++, los objetos se tratan de la misma (en la mayoría de formas) como los tipos de valores básicos. Los objetos pueden vivir en la pila o en el almacenamiento estático, y se pasa por valor. Cuando se declara una cadena en una función, que asigna en la pila sin embargo muchos bytes del objeto de cadena toma. El propio objeto de cadena hace uso de memoria dinámica para almacenar los caracteres reales, pero eso es transparente para el usuario. La otra cosa a tener en cuenta es que cuando sale de la función y la cadena que declaradas ya no está en su alcance, toda la memoria que se utiliza es liberada. No hay necesidad de recolección de basura (RAII es su mejor amigo).

En el ejemplo:

string a;
a = "less"; 
a = "moreeeeeee";

Esto pone un bloque de memoria en la pila y lo nombra a, entonces se llama al constructor y se inicializa a una cadena vacía. El compilador almacena los bytes de la sección .rdata de su exe "menos" y "moreeeeeee" en (creo). Encadenar una tendrá algunos campos, como un campo de longitud y un char * (estoy simplificando en gran medida). Cuando se asigna "menos" a un, el operador = () método se llama. Dinámicamente asigna memoria para almacenar el valor de entrada, y luego lo copia. Cuando más tarde asigna "moreeeeeee" a un, el operador = () método se llama de nuevo y se reasigna memoria suficiente para sostener el nuevo valor si es necesario, y luego lo copia en a la memoria intermedia interna.

Cuando sale del alcance de cadena A, el destructor se llama cadena y de la memoria que se asigna de forma dinámica para mantener a los personajes reales se libera. A continuación, el puntero de pila es decrementado y la memoria que llevó a cabo una ya no es "sobre" la pila.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top