2011-01-17 13 views
17

abbiamo un'applicazione desktop multithread in C++ (MFC). Attualmente gli sviluppatori usano CString o std :: string, probabilmente a seconda del loro umore. Quindi vorremmo scegliere una singola implementazione (probabilmente qualcosa di diverso da questi due).Quali classi stringa usare in C++?

CString di MFC si basa sull'idioma copy-on-write (COW) e alcune persone ritengono che ciò sia inaccettabile in un ambiente con multithreading (e probabilmente riferimento a this article). Non sono convinto da tali affermazioni, poiché i contatori atomici sembrano essere abbastanza veloci, e anche questo sovraccarico è in qualche modo compensato da una riduzione delle ridistribuzioni della memoria.

Ho imparato che l'implementazione di std :: string dipende dal compilatore - non è COW in MSVC ma è, o era in gcc. Per quanto ho capito, il nuovo standard C++ 0x risolverà questo problema richiedendo un'implementazione non COW e risolvendo alcuni altri problemi, come i requisiti di buffer contigui. Quindi in effetti std :: string sembra non ben definito a questo punto ...

Un rapido esempio di ciò che non mi piace di std :: string: impossibile restituire una stringa da una funzione senza ridistribuzioni eccessive (copia costruttore se restituisce per valore, e nessun accesso al buffer interno per ottimizzarlo così "ritorno per riferimento" es. std::string& Result non aiuta). Posso farlo con CString restituendo per valore (nessuna copia dovuta a COW) o passando per riferimento e accedendo direttamente al buffer. Ancora una volta, C++ 0x in soccorso con i suoi riferimenti rvalue, ma non avremo C++ 0x nella funzione più vicina.

Quale classe di stringa dovremmo utilizzare? La COW può davvero diventare un problema? Esistono altre implementazioni efficienti comunemente utilizzate delle stringhe? Grazie.

MODIFICA: Al momento non utilizziamo unicode ed è improbabile che ne avremo bisogno. Tuttavia, se c'è qualcosa che supporta facilmente unicode (non a costo di ICU ...), sarebbe un vantaggio.

+0

La maggior parte delle persone supporrà che non sia UNICODE. È giusto? –

+0

@baris_a: grazie, lo renderò più chiaro –

+4

Sei a conoscenza di [RVO] (http://en.wikipedia.org/wiki/Return_value_optimization)? Ciò potrebbe placare alcune delle tue preoccupazioni sulle stringhe come valori di ritorno. Si potrebbe anche voler leggere [Vuoi velocità? Passa per valore.] (Http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/) –

risposta

16

Vorrei utilizzare std::string.

  • promuovere la dissociazione da MFC
  • migliore interazione con C++ le librerie esistenti

il "ritorno per valore" questione è per lo più un non-problema. I compilatori sono molto bravi a eseguire Ottimizzazione del valore restituito (RVO) che in realtà elimina la copia nella maggior parte dei casi quando si restituisce il valore. In caso contrario, di solito è possibile modificare la funzione.

COW è stata respinta per un motivo: non scala (bene) e l'aumento così sperati in termini di velocità non è stato realmente misurata (vedi Herb Sutter di article). Le operazioni atomiche non sono così economiche come sembrano. Con il mono-processore mono-core era facile, ma ora multi-core sono commodity e multi-processori sono ampiamente disponibili (per i server). In tali architetture distribuite ci sono più cache, che devono essere sincronizzate, e più è distribuita l'architettura, più costose sono le operazioni atomiche.

CString attrezzo Ottimizzazione stringa piccola? È un trucco semplice che consente a una stringa di non allocare memoria per le stringhe di piccole dimensioni (in genere pochi caratteri). Molto utile perché risulta che la maggior parte delle stringhe sono in effetti piccole, quante stringhe nella tua applicazione sono lunghe meno di 8 caratteri?

Quindi, a meno che non mi presenti un vero benchmark che mostra chiaramente un guadagno netto nell'uso di CString, preferirei attenermi allo standard: è standard e probabilmente è ottimizzato meglio.

+0

Hai ri-citato lo stesso articolo, carino :) Sembra piuttosto sorpassato, però. Sto parlando di sistemi MFC e desktop, dove i contatori atomici sono in realtà abbastanza veloci (questo potrebbe cambiare in futuro, ma chissà). SSO è una buona cosa, ma non è contro COW, giusto? Non sto sostenendo che CString sia migliore di std :: string, sto solo cercando una risposta alla mia domanda. –

+3

"Migliore interazione con le librerie C++ esistenti": se la libreria C++ esistente è MFC, CString offre un'interazione migliore. –

+1

@ 7vies: SSO è perfettamente compatibile con COW, ma non ho mai visto l'intrinseco di CString quindi non so se lo utilizza o meno. Ho cercato di rispondere alla tua domanda: ho affrontato i tuoi problemi prestazionali e ho cercato di chiarire che, data la scelta tra una versione standard e una versione personalizzata, avrei scelto lo standard a meno che non ci fosse un problema di prestazioni. –

0

Si consiglia di utilizzare std :: basic_string come base del modello di stringa generale a meno che non vi sia una buona ragione per fare diversamente. Dico basic_string perché se si gestiscono caratteri a 16 bit si usa wstring.

Se avete intenzione di utilizzare TCHAR probabilmente è necessario definire tstring come basic_string e potrebbe desiderare di implementare una classe tratti per troppo utilizzare funzioni come _tcslen ecc

5

In realtà, la risposta può essere "Dipende" . Ma, se si utilizza MFC, IMHO, CString, l'utilizzo sarebbe migliore. Inoltre, è possibile utilizzare anche CString con contenitori STL. Ma, porterò ad un'altra domanda, dovrei usare contenitori stl o contenitori MFC con CString? L'utilizzo di CString fornirà agilità alla tua applicazione, ad esempio nelle conversioni Unicode.

MODIFICA: Inoltre, se si utilizzano chiamate API WIN32, le conversioni CString saranno più semplici.

MODIFICA: CString ha un GetBuffer() e relativi metodi che consentono di modificare direttamente il buffer.

MODIFICA: ho utilizzato CString nel nostro wrapper SQLite e la formattazione di CString è più semplice.

bool RS::getString(int idx, CString& a_value) { 

//bla bla 

     if(getDB()->getEncoding() == IDatabase::UTF8){ 
      a_value.Format(_T("%s"), sqlite3_column_text(getCommand()->getStatement(), idx)); 
     }else{ 
      a_value.Format(_T("%s"), sqlite3_column_text16(getCommand()->getStatement(), idx)); 
     } 
     return true; 
} 
+5

In nessun modo useremo i contenitori MFC :) usiamo già STL per quello –

+0

'CString :: GetBuffer' ==' e MyStdString [0] '. Non c'è molto di una discussione lì. Per quanto riguarda la formattazione, questo è ciò che 'std :: stringstream' è per. –

+1

@Billy: come puoi menzionare le corde di SGI e & MyStdString [0] allo stesso tempo? (non è ancora C++ 0x per tutti, giusto?) Inoltre non sono d'accordo su 'std :: stringstream', dato che è un modo terribile per formattare le stringhe, specialmente quando si tratta di modificatori. Boost :: format sembra meglio –

1

Non conosco altre implementazioni di stringhe comuni, tutti soffrono degli stessi limiti di linguaggio in C++ 03. O offrono qualcosa di specifico, come il modo in cui i componenti ICU sono ottimi per Unicode, sono davvero vecchi come CString, oppure std :: string li supera.

Tuttavia, è possibile utilizzare la stessa tecnica utilizzata da MSVC9 SP1 STL, ovvero "swaptimization", che è l'ottimizzazione con il nome più esilarante di sempre.

void func(std::string& ref) { 
    std::string retval; 
    // ... 
    std::swap(ref, retval); // No copying done here. 
} 

Se hai ottenuto una classe stringa personalizzata che non assegnava nulla è costruttore di default (o controllato l'implementazione STL), allora swaptimizing sarebbe non garantire allocazioni ridondanti. Ad esempio, il mio STL MSVC utilizza SSO e non alloca memoria di heap per impostazione predefinita, pertanto, in base alla velocità di swap di quanto sopra, non ottengo allocazioni ridondanti.

È possibile migliorare notevolmente anche le prestazioni semplicemente non utilizzando un'allocazione di heap costosa. Esistono allocatori progettati per allocazioni temporanee e puoi sostituire l'allocatore utilizzato nell'implementazione STL preferita con uno personalizzato. Puoi ottenere cose come i pool di oggetti da Boost o aprire un'arena di memoria. È possibile ottenere prestazioni dieci volte migliori rispetto a una normale nuova allocazione.

-2

std::string è di solito conteggiato con riferimento, quindi il valore pass-by è ancora un'operazione a basso costo (e ancora di più con il riferimento a roba in C++ 0x). La mucca è attivato solo per le stringhe che hanno più riferimenti che puntano ad esse, cioè .:

std::string foo("foo"); 
std::string bar(foo); 
foo[0] = 'm'; 

passerà attraverso il percorso di mucca. Poiché la COW si verifica all'interno di operator[], è possibile forzare una stringa per utilizzare un buffer privato utilizzando i suoi metodi (non costanti) operator[]() o begin().

+0

Per quanto ne so, non è un riferimento contato in MSVC, correggimi se sbaglio. Siamo anche bloccati con una vecchia versione MSVC. –

+2

In realtà lo standard C++ 0x vieta l'uso di COW. –

+2

@Matthieu: hai un riferimento standard per questo? (Non sto dicendo che ti stai sbagliando, ma non riesco a trovarlo) –

1

Suggerirei di prendere una decisione "per DLL".Se le DLL dipendono fortemente da MFC (ad esempio, il livello della GUI), dove sono necessarie molte chiamate MFC con i parametri CString, utilizzare CString. Se si dispone di DLL in cui l'unica cosa da utilizzare MFC sarebbe la classe CString, utilizzare invece std::string. Naturalmente, avrai bisogno della funzione di conversione tra entrambe le classi, ma ho il sospetto che tu abbia già risolto il problema.

1

Dico sempre go per std::string. Come accennato, RVO e NVRO renderanno le copie a buon mercato, e quando si do si passa a C++ 0x alla fine, si ottiene un buon incremento delle prestazioni dalla semantica del movimento, senza fare nulla. Se si desidera prendere qualsiasi codice e utilizzarlo in un progetto non ATL/MFC, non è possibile utilizzare CString, ma std::string sarà lì, quindi sarà molto più semplice. Infine, hai menzionato in un commento che usi contenitori STL invece di contenitori MFC (buona mossa). Perché non rimanere coerenti e usare anche la stringa STL?

+3

I contenitori MFC sono inutilizzabili, quindi era semplice :) CString, d'altra parte, è molto comodo da utilizzare, come è stato menzionato in altre risposte - ad esempio, CString :: Format è molto utile (so che ci sono modi per fare una cosa simile con std :: string, ma non sono così convenienti). –