在C ++中,从函数返回函数local 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...???
答案 0 :(得分:69)
没有。事实并非如此。即使mystring
超出范围并被销毁,ret
也有一个mystring副本,因为函数MyFunc
按值返回。
答案 1 :(得分:21)
如果您的代码如下,则会出现问题:
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;
}
这将为编译器提供提示以进行更多优化,因此它可以减少一个字符串副本(RVO)。
答案 2 :(得分:6)
如上所述,复制了std :: string。因此,即使原始局部变量超出范围,调用者也会获得std :: string的副本。
我认为阅读RVO可以完全清除你的困惑。在这种情况下,它被准确地称为NRVO(命名为RVO),但精神是相同的。
奖金阅读:使用RVO的问题在于它不是世界上最灵活的东西。 C ++ 0x的一大嗡嗡声是rvalue references,它打算解决这个问题。
答案 3 :(得分:5)
你试过吗? 返回时会复制该字符串。 那么这是官方的,实际上副本可能已经过优化,但无论哪种方式都可以安全使用。
答案 4 :(得分:3)
嗯,在MyFunc()之后,ret将具有mystring值。如果按值返回结果,则通过复制本地对象来构造临时对象。
至于我,C++ FAQ Lite的这些部分中有关于该主题的一些有趣的细节。
答案 5 :(得分:2)
这取决于用例。如果实例应该对字符串负责,则应该通过const引用返回stings。问题是,如果没有要返回的对象,该怎么办。使用指针,可以使用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;
}
祝你好运, Valentin Heinitz
答案 6 :(得分:2)
之前的答案都没有包含关键概念。这个概念是移动语义。 std::string
类具有 move
构造函数,这意味着它具有移动语义。 移动语义意味着在函数返回时不会将对象复制到不同的位置,从而提供更快的函数执行时间。
尝试单步调试返回 std::string
的函数并检查即将返回的对象的内部结构。您将看到成员字段指针地址 xxx
。然后,检查接收函数返回值的 std::string
变量。您将在该对象中看到相同的指针地址 xxx
。
这意味着,女士们,先生们,没有发生复制。这是移动语义,上帝保佑美国!