返回函数本地的 std::string 的最佳方法

Posted

技术标签:

【中文标题】返回函数本地的 std::string 的最佳方法【英文标题】:best way to return an std::string that local to a function 【发布时间】:2011-04-27 23:26:16 【问题描述】:

在 C++ 中,从函数返回函数本地 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...???

【问题讨论】:

您按值返回,而不是按引用返回。因此,'mystring' 消失并不重要。 【参考方案1】:

没有。那不是真的。即使mystring 已经超出范围并被销毁,ret 也有一个 mystring 的副本,因为函数 MyFunc 按值返回。

【讨论】:

从技术上讲,它不是副本。 当原始字符串对象超出范围并被破坏时,内部(堆)数据是否也会被删除?如果是这种情况,返回是否会进行深层复制?【参考方案2】:

如果你的代码是这样的,就会有问题:

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)。

【讨论】:

为什么要显式转换?这不是让问题变得模糊吗?只是做return "foo"; 就像一个魅力。【参考方案3】:

如前所述,std::string 被复制。因此,即使原来的局部变量已经超出范围,调用者也会得到 std::string 的副本。

我认为阅读RVO 可以完全消除您的困惑。在这种情况下,它被准确地称为 NRVO(命名为 RVO),但精神是相同的。

额外阅读:使用 RVO 的问题在于它不是世界上最灵活的东西。 C++0x 的一大热点是rvalue references,它旨在解决这个问题。

【讨论】:

RVO 有两种风格:URVO(未命名的 RVO)与临时变量相关,NRVO(命名的 RVO)与局部变量相关。 URVO 通常更简单(对于编译器)。 NRVO 更加困难,因为使用命名变量,您可以有各种 return 语句,每个语句都返回一个不同的变量。此时优化器必须选择 1 变量作为被优化的变量,并且所有其他路径都将产生一个副本。【参考方案4】:

你试过了吗? 字符串在返回时被复制。 好吧,这是官方的说法,实际上副本可能已被优化掉,但无论哪种方式都可以安全使用。

【讨论】:

实际上,对于像 std::string 这样具有非平凡复制构造函数的类,它无法被优化掉,但它会在类确实具有平凡的情况下复制构造函数。 “在类的情况下......使用非平凡的构造函数,它不能被优化掉”——哦,但它可以,而且通常是。试试这个:std::string * sp; std::string func() std::string s("bla"); sp = &s;返回 s; int main() std::string s = func(); if(sp == &s) std::cout PigBen 所写的内容当然会导致未定义的行为,但即使在已定义的情况下,编译器有时也有权忽略副本。 Google for RVO 和 NRVO。【参考方案5】:

之前的答案都没有包含 关键概念。这个概念就是移动语义std::string 类具有move 构造函数,这意味着它具有移动语义移动语义意味着对象不会在函数返回时被复制到不同的位置,从而提供更快的函数执行时间。

尝试将调试单步调试到返回std::string 的函数中,并检查即将返回的对象的内部结构。您将看到成员字段指针地址xxx。然后,检查接收函数返回值的std::string 变量。您将在该对象中看到相同的指针地址xxx

这意味着,女士们,先生们,没有复制。这是移动语义,上帝保佑美国!

【讨论】:

【参考方案6】:

嗯,在 MyFunc() 之后 ret 将具有 mystring 的值。如果按值返回结果,则通过复制本地对象来构造一个临时对象。

就我而言,C++ FAQ Lite 的这些部分中有一些关于该主题的有趣细节。

【讨论】:

其中有哪些有趣的细节?你能说得更具体点吗?【参考方案7】:

这取决于用例。如果一个实例应该对一个字符串负责,那么字符串应该由一个 const 引用返回。问题是,如果没有要返回的对象,该怎么办。使用指针,无效对象可以使用 0 来表示。这样的“空对象”也可以与引用一起使用(例如,代码 sn-p 中的 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;

【讨论】:

以上是关于返回函数本地的 std::string 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

返回 const std::string& 的方法应该返回 const std::string_view 吗?

std::string::c_str() 覆盖函数返回的前一个

C++string类型与C语言字符数组的转换 std::string.c_str()函数

c ++如何处理返回NULL或std :: string的方法?

C++ 静态构造函数返回一个容器

std::to_string(double) 的精度