2010-10-20 4 views
47

В C++, что лучший способ вернуть функцию local std :: string variable из функции?лучший способ вернуть std :: string, который является локальным для функции

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

Вы возвращаетесь по значению, а не по ссылке. Как таковое, не имеет значения, что «мистификация» исчезла. –

ответ

64

Нет. Это неправда. Даже если mystring вышел из сферы действия и уничтожен, ret имеет копию mystring, поскольку функция MyFunc возвращает значение.

5

Вы пробовали? Строка копируется при ее возврате. Ну, это официальная строка, на самом деле копия, вероятно, оптимизирована, но в любом случае она безопасна в использовании.

+0

На самом деле, в случае класса, такого как std :: string с нетривиальным конструктором копирования, его нельзя оптимизировать, но это будет в тех случаях, когда класс имеет тривиальный конструктор копии. –

+4

«в случае класса ... с нетривиальным конструктором его нельзя оптимизировать» - О, но он может и обычно есть. Попробуйте это: 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"; else std :: cout << "BOO"; } - На моем компиляторе (VS) Он печатает YAY. –

+2

то, что написал PigBen, конечно, приводит к неопределенному поведению, но даже в определенных обстоятельствах компилятор имеет право иногда копировать копию. Google для RVO и NRVO. – avakar

5

Как уже упоминалось, std :: string копируется. Таким образом, даже исходная локальная переменная вышла за пределы области действия, вызывающая сторона получает копию строки std ::.

Я думаю, что чтение на RVO может полностью очистить ваше замешательство. В этом случае он точно упоминается как NRVO (Named RVO), но дух тот же.

Оценка бонуса: Проблема с использованием RVO заключается в том, что это не самая гибкая вещь в мире. Одним из больших жуков C++ 0x является rvalue references, который намеревается решить эту проблему.

+0

Есть два варианта RVO: URVO (Без названия RVO) относится к временным и NRVO (Именованный RVO) относится к локальным переменным. URVO обычно проще (для компилятора). NRVO сложнее, потому что с именованными переменными вы можете иметь различный оператор 'return', каждый из которых возвращает другую переменную. На этом этапе оптимизатор должен выбрать ** 1 ** переменную, поскольку тот, который оптимизирован, и все остальные пути приведут к копированию. –

18

Там будет проблемой, если ваш код, как:

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

Итак, как вы написали это в порядке. Только один совет - если вы можете создать строку, как это, я имею в виду - вы можете сделать это в одной строке, иногда лучше сделать это следующим образом:

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

Или, если это более «сложный», напр :

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

Это даст подсказку на свой компилятор, чтобы сделать больше оптимизации, так что это может сделать один меньше копию строки (РВО).

+1

Почему явный приведение, во всяком случае? Разве это не проблема? Просто выполнение 'return 'foo"; 'работает как шарм. –

+0

Да, это то же самое. Хорошо, я отредактирую его, это более ясно (: –

3

Ну, ret будет иметь значение mystring после MyFunc(). В случае возврата результата по значению временный объект создается путем копирования локального объекта.

Что касается меня, есть некоторые интересные подробности по теме в этих разделах C++ FAQ Lite.

2

Это зависит от используемого варианта. Если экземпляр должен нести ответственность за строку, жало должно быть возвращено ссылкой const. Проблема в том, что делать, если нет объекта для возврата. С указателями недопустимый объект может быть сигнализирован с использованием 0. Такой «нулевой объект» может также использоваться со ссылками (например, NullString в фрагменте кода). Из-за причины лучшего способа оповестить недопустимое возвращаемое значение бросает исключения.

Другой вариант использования - если ответственность за строку передается вызывающему. В этом случае следует использовать auto_ptr. В приведенном ниже коде показаны все варианты использования.

#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; 
} 

С наилучшими пожеланиями, Валентин Heinitz