2010-10-20 6 views
47

In C++ quale è il modo migliore per restituire una funzione std :: string variabile dalla funzione?il modo migliore per restituire uno std :: string locale a una funzione

std::string MyFunc() 
{ 
    std::string mystring("test"); 
    return mystring; 

} 

std::string ret = MyFunc(); // ret has no value because mystring has already gone out of scope...??? 
+16

State ritornando per valore, non con riferimento. In quanto tale, non importa che "mystring" sia sparito. –

risposta

64

No. Non è vero. Anche se mystring è andato fuori uso ed è stato distrutto, ret ha una copia di mystring poiché la funzione MyFunc restituisce per valore.

5

Hai provato? La stringa viene copiata quando viene restituita. Bene, questa è la linea ufficiale, in realtà la copia è probabilmente ottimizzata, ma in ogni caso è sicuro da usare.

+0

In realtà, nel caso di una classe come std :: string con un costruttore di copia non banale, non può essere ottimizzato, ma sarà nei casi in cui la classe ha un costruttore di copia banale. –

+4

"nel caso di una classe ... con un costruttore non banale, non può essere ottimizzato via" - Oh, ma può, e di solito lo è. Prova questo: std :: string * sp; std :: string func() { std :: string s ("bla"); sp = & s; return s; } int main() { std :: string s = func(); if (sp == & s) std :: cout << "YAY"; altro std :: cout << "BOO"; } - Sul mio compilatore (VS) stampa YAY. –

+2

ciò che ha scritto PigBen ha come risultato un comportamento indefinito, ma anche in determinate circostanze il compilatore ha il diritto di eludere la copia a volte. Google per RVO e NRVO. – avakar

5

Come già detto, la stringa std :: viene copiata. Quindi anche la variabile locale originale è uscita dal campo di applicazione, il chiamante ottiene una copia di std :: string.

Penso che leggere su RVO possa cancellare completamente la tua confusione. In questo caso, viene indicato con precisione come NRVO (Named RVO) ma lo spirito è lo stesso.

Lettura bonus: Il problema con l'utilizzo di RVO è che non è la cosa più flessibile al mondo. Uno dei grandi buzz di C++ 0x è rvalue references che intende risolvere questo problema.

+0

Esistono due tipi di RVO: URVO (RVO senza nome) si riferisce ai provvisori e NRVO (denominato RVO) si riferisce a variabili locali. URVO è solitamente più semplice (per il compilatore). NRVO è più difficile, perché con variabili denominate potresti avere varie istruzioni 'return', ognuna delle quali restituisce una variabile diversa. A questo punto l'ottimizzatore deve selezionare ** 1 ** variabile come quella ottimizzata, e tutti gli altri percorsi produrranno una copia. –

18

Ci sarà un problema se il codice è simile:

std::string& MyFunc() 
{ 
    std::string mystring("test"); 
    return mystring; 
} 

Quindi, il modo che hai scritto è OK. Solo un consiglio - se si può costruire la stringa in questo modo, voglio dire - lo si può fare in una fila, a volte è meglio farlo in questo modo:

std::string MyFunc() 
{ 
    return "test"; 
} 

O se è più "complicato", ad es :

std::string MyFunct(const std::string& s1, 
        const std::string& s2, 
        const char* szOtherString) 
{ 
    return std::string("test1") + s1 + std::string(szOtherString) + s2; 
} 

Questo darà un suggerimento al compilatore di fare di più di ottimizzazione, in modo che possa fare uno in meno copia della stringa (RVO).

+1

Perché il cast esplicito, comunque? Non è questo il problema? Semplicemente facendo "return" pippo ";" funziona come un incantesimo. –

+0

Sì, è lo stesso. OK, lo modifico, è più chiaro (: –

3

Bene, ret avrà un valore di mystring dopo MyFunc(). In caso di restituzione del risultato per valore viene costruito un oggetto temporaneo copiando quello locale.

Per quanto mi riguarda, ci sono alcuni dettagli interessanti sull'argomento in queste sezioni di C++ FAQ Lite.

2

Dipende dal caso d'uso. Se l'istanza dovesse mantenere la responsabilità della stringa, le punture dovrebbero essere restituite con riferimento const. Il problema è, cosa fare, se non ci sono oggetti da restituire. Con i puntatori l'oggetto non valido può essere segnalato usando 0. Tale "oggetto nullo" potrebbe anche essere usato con riferimenti (ad esempio NullString nel frammento di codice). Il modo migliore per segnalare un valore di ritorno non valido genera eccezioni.

Un altro caso d'uso è se la responsabilità per la stringa viene trasferita al chiamante. In questo caso dovrebbe essere usato auto_ptr. Il codice seguente mostra tutti questi casi d'uso.

#include <string> 
#include <memory> //auto_ptr 
#include <iostream> 
using std::string; 
using std::auto_ptr; 
using std::cout; 
using std::endl; 

static const string NullString("NullString\0"); 


///// Use-Case: GETTER ////////////////// 
//assume, string should be found in a list 
// and returned by const reference 

//Variant 1: Pseudo null object 
const string & getString(bool exists) { 
    //string found in list 
    if(exists) { 
    static const string str("String from list"); 
    return str; 
    } 
    //string is NOT found in list 
    return NullString; 
} 

//Variant 2: exception 
const string & getStringEx(bool available) { 
    //string found in list 
    if(available) { 
    static const string str("String from list"); 
    return str; 
    } 

    throw 0; //no valid value to return 
} 

///// Use-Case: CREATER ///////////////// 
auto_ptr<string> createString(bool ok) 
{ 
    if(ok){ 
    return auto_ptr<string>(new string("A piece of big text")); 
    }else{ 
    return auto_ptr<string>(); 
    } 
} 

int main(){ 
    bool ok=true, fail=false; 
    string str; 
    str = getString(ok); 
    cout << str << ", IsNull:"<<(str == NullString)<<endl; 
    str = getString(fail); 
    cout << str << ", IsNull:"<<(str == NullString)<<endl; 

    try{ 
    str = getStringEx(ok); 
    cout << str <<endl; 
    str = getStringEx(fail); 
    cout << str <<endl; //line won't be reached because of ex. 
    } 
    catch (...) 
    { 
    cout << "EX: no valid value to return available\n"; 
    } 

    auto_ptr<string> ptext = createString(ok); 
    if (ptext.get()){ 
    cout << *ptext << endl; 
    } else { 
     cout << " Error, no text available"<<endl; 
    } 

    ptext = createString(fail); 
    if (ptext.get()){ 
    cout << *ptext << endl; 
    } else { 
     cout << " Error, no text available"<<endl; 
    } 

return 0; 
} 

Con i migliori saluti, Valentin Heinitz

Problemi correlati