Domanda

Ho seguito domande riguardanti le stringhe in C ++

1 >>, che è una scelta migliore (prestazioni in considerazione) e perché?

1.

string a;
a = "hello!";

o

2.

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

2 >>

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

esattamente come la gestione della memoria è gestita in C ++ quando una stringa più grande viene copiato in una stringa più piccola? Sono C ++ stringhe mutevole?

È stato utile?

Soluzione

Tutte le seguenti è ciò che un compilatore ingenuo avrebbe fatto. Naturalmente fino a quando non cambia il comportamento del programma, il compilatore è libero di fare qualsiasi ottimizzazione.

string a;
a = "hello!";

Prima si inizializza una per contenere la stringa vuota. (Impostare la lunghezza di 0, e una o due altre operazioni). Poi si assegna un nuovo valore, sovrascrivendo il valore di lunghezza che è stato già impostato. Può anche essere necessario effettuare un controllo per quanto grande il buffer corrente è, e anche più memoria deve essere allocata.

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

Calling nuovo richiede il sistema operativo e l'allocatore di memoria per trovare un pezzo privo di memoria. Questo è lento. Poi si inizializza immediatamente, in modo da non si assegna nulla due volte o richiedere il buffer per essere ridimensionata, come si fa nella prima versione. Poi succede qualcosa di brutto, e si dimentica di chiamare cancellare, e si dispone di una perdita di memoria, oltre per una stringa che è estremamente lento per allocare. Quindi questo è un male.

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

Come nel primo caso, in primo luogo inizializzare una per contenere la stringa vuota. Poi si assegna una nuova stringa, e poi un altro. Ognuno di questi possono richiede una chiamata a nuova per allocare più memoria. Ogni riga richiede anche lunghezza, ed eventualmente altre variabili interne da assegnare.

Di solito, devi allocare in questo modo:

string a = "hello";

Una linea, eseguire l'inizializzazione una volta, piuttosto che prima default-inizializzazione, e assegnando il valore desiderato.

Si riduce al minimo gli errori perché non si dispone di una stringa vuota senza senso qualsiasi punto del programma. Se esiste la stringa, che contiene il valore che si desidera.

A proposito di gestione della memoria, google Raii. In breve, stringa le chiamate new / delete internamente per ridimensionare il suo buffer. Questo significa che non bisogno di allocare una stringa con il nuovo. L'oggetto stringa ha una dimensione fissa, ed è progettato per essere allocato in pila, in modo che il distruttore è automaticamente chiamato quando si passa nell'ambito. Il distruttore garantisce quindi che qualsiasi memoria allocata viene liberata. In questo modo, non è necessario utilizzare la nuova / cancellare il codice utente, il che significa che non ci siano perdite di memoria.

Altri suggerimenti

E 'quasi mai necessario o auspicabile per dire

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

Dopo tutto, si sarebbe (quasi) mai dire:

int * i = new int(42);

Si dovrebbe invece dire

string s( "hello" );

o

string s = "hello";

E sì, le stringhe C ++ sono mutabili.

C'è un motivo specifico per cui si utilizza costantemente l'assegnazione al posto di intialization? Cioè, perché non si scrive

string a = "Hello";

, ecc.? Questo evita una costruzione di default e rende solo più senso semanticamente. Creazione di un puntatore a una stringa solo per il gusto di allocazione sul mucchio non è mai significativo, vale a dire il vostro caso 2 non ha senso ed è leggermente meno efficiente.

Per quanto riguarda la tua ultima domanda, sì, le stringhe in C ++ sono mutabili a meno const dichiarato.

string a;
a = "hello!";

2 operazioni: chiama il costruttore di default std: string () e quindi chiama l'operatore :: =

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

una sola operazione: chiama il costruttore di std:. String (const char *) ma non si deve dimenticare di rilasciare il puntatore

Che dire      stringa di un ( "ciao");

Nel caso 1.1, la stringa di membri (che comprendono puntatore ai dati) sono tenuti in stack e la memoria occupata da l'istanza della classe viene liberata quando a va fuori del campo di applicazione.

In caso 1.2, memoria per i membri è assegnato dinamicamente dal mucchio troppo.

Quando si assegna un char* costante per una stringa, la memoria che conterrà i dati saranno realloc'ed per adattarsi ai nuovi dati.

È possibile visualizzare la quantità di memoria allocata chiamando string::capacity().

Quando si chiama string a("hello"), la memoria viene allocata nel costruttore.

Sia costruttore e call operatore di assegnazione stessi metodi internamente alla memoria allocata e copia nuovi dati lì.

Se si guarda alla docs per la classe string STL (I credere la documentazione SGI sono conformi alle specifiche), molti dei metodi lista garanzie di complessità. Credo che molte delle garanzie di complessità sono lasciata intenzionalmente vaga per consentire diverse implementazioni. Credo che alcune implementazioni effettivamente utilizzare un approccio copy-on-modifica in modo tale che l'assegnazione di una stringa ad un altro è un'operazione a tempo costante, ma si può essere richiesto un pagamento imprevisto quando si tenta di modificare uno di quei casi. Non sono sicuro se questo è ancora vero in STL moderna però.

Si dovrebbe anche controllare la funzione capacity(), che vi dirà la stringa di lunghezza massima si può mettere in una determinata istanza di stringa prima che sarà costretto a riallocare la memoria. È inoltre possibile utilizzare reserve() per causare una ridistribuzione a un importo specifico, se sai che stai andando a memorizzare una stringa di grandi dimensioni nella variabile in un secondo momento.

Come altri hanno detto, per quanto i vostri esempi andare, si dovrebbe davvero favorire l'inizializzazione rispetto ad altri approcci al fine di evitare la creazione di oggetti temporanei.

La creazione di una stringa direttamente nel mucchio di solito non è una buona idea, proprio come la creazione di tipi di base. Non vale la pena dal momento che l'oggetto può facilmente rimanere nello stack ed ha tutti i costruttori di copia e l'assegnazione operatore necessaria per una copia efficiente.

Lo std. Stringa stessa ha un buffer in mucchio che possono essere condivise da più stringa seconda dell'implementazione

Per intsance, con l'attuazione STL di Microsoft si potrebbe fare che:

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

Ed entrambi stringa avrebbe condiviso lo stesso buffer fino a quando è stata modificata:

a = "Something else!";

Ecco perché è stato molto male per memorizzare il c_str () per quest'ultimo uso; c_str () garentee solo validità fino un'altra chiamata a tale oggetto stringa è fatto.

Questo ha portato ad errori di concorrenza molto brutto che hanno richiesto questa funzionalità di condivisione per essere spento con un definire se li si è utilizzato in un'applicazione multithread

Molto probabilmente

   string a("hello!");

è più veloce di qualsiasi altra cosa.

Per chi proviene da Java, giusto? In C ++, gli oggetti vengono trattati allo stesso (nella maggior parte dei modi) come i tipi di valore di base. Oggetti possono vivere in pila o in custodia statica, ed essere passato per valore. Quando si dichiara una stringa in una funzione, che alloca in pila tuttavia molte byte dell'oggetto stringa prende. L'oggetto stringa stessa fa uso di memoria dinamica per memorizzare i caratteri effettivi, ma questo è trasparente per voi. L'altra cosa da ricordare è che quando la funzione esce e la stringa dichiarato non è più in campo di applicazione, tutta la memoria è utilizzata viene liberato. Non c'è bisogno di garbage collection (RAII è il tuo migliore amico).

Nel tuo esempio:

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

Questo pone un blocco di memoria in pila e lo nomina un, allora il costruttore viene chiamato e viene inizializzato ad una stringa vuota. I negozi del compilatore il byte per la sezione .rdata del vostro exe "meno" e "moreeeeeee" di (credo). String una avrà alcuni campi, come un campo di lunghezza e un char * (sto semplificando notevolmente). Quando si assegna "meno" per una, l'operatore = () metodo viene chiamato. Si alloca dinamicamente la memoria per memorizzare il valore di ingresso, poi lo copia. Quando più tardi assegna "moreeeeeee" ad un, l'operatore = () viene di nuovo chiamato e si rialloca memoria sufficiente per contenere il nuovo valore, se necessario, poi lo copia in al buffer interno.

Quando uscite scope stringa di un, il distruttore stringa è chiamato e la memoria che è stata allocata dinamicamente per contenere i caratteri effettivi viene liberato. Poi lo stack pointer viene decrementato e la memoria che ha tenuto una non è più "in" stack.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top