在C++中,返回字符串的最佳方法是什么?

70
我的问题很简单:如果我有一个类Man,并且我想定义一个返回人名的成员函数,下面两种变体中我应该选择哪一种?
第一种:
string name();

第二:

void name(/* OUT */ string &name);

第一种变体有点低效,因为它会产生不必要的拷贝(局部变量 -> 返回值 -> 赋值左侧的变量)。
第二种变体看起来相当高效,但让我写起来感觉很痛苦。
string name;
john.name(name);

而不是简单的

string name(john.name());

那么,我应该选择哪个变体,效率和方便/可读性之间的适当权衡是什么?

7
附注:由于 name() 看起来像一个查询函数,所以请将其更改为 conststring name() const; - hmjd
3
@JamesKanze,那不是签名的一部分,而是我的评论,后面跟着一个“:”。 - hmjd
如果只是简单的getter访问相应的成员变量,那么使用const std::string&可能是一个更好的选择。 - Christian Rau
@hmjd 很抱歉。在我使用的字体中,冒号根本没有显示出来。 - James Kanze
在 RVO 上的众多问题之一:https://dev59.com/g2sz5IYBdhLWcg3w47-m - Matthieu M.
显示剩余4条评论
7个回答

55

这是一个好问题,而且你提出这个问题表明你正在关注你的代码。然而,在这种情况下,有一个简单的方法可供选择。

第一种干净的方法是正确的做法。编译器将消除大部分不必要的拷贝(通常在有意义的地方)。

编辑(2016年6月25日)

不幸的是,David Abaraham的网站已经关闭了几年,那篇文章也已经失踪了(没有archive.org的副本)。为了档案目的,我上传了我的本地副本作为PDF,并且可以在这里找到它


是的,这篇文章写得非常好,确实言简意赅。 - Mahmoud Al-Qudsi
谢谢!我已经阅读了文章,一开始感到非常困惑,但真的帮助我弄清楚了事情。现在我需要优化我的代码 =) - tonytony
当然,对于非常重的变量,我总是会使用“第二种变量”。我觉得这种方法甚至可以自我注释,因为读者会假定数据类型是重量级的。这只是我的个人意见。 - user606723
这篇文章是关于 RVO 的,但值得一提的是在 C++11 中不再需要 RVO,因为移动语义将会启用。 - Luca Venturini

27
使用第一个变量:
string name();

编译器很可能会优化掉任何不必要的复制。请参阅返回值优化
在C++11中,移动语义意味着即使编译器没有执行RVO,您也不需要执行完整的复制。请参阅移动语义
还要记住,如果备选方案是
void name(std::string& s);

那么你必须非常清楚地指定s可能发生的情况以及传递给函数时它可以具有的值,可能需要进行大量的有效性检查,或者简单地完全覆盖输入。


9

如果你想为类的字段创建一个getter,可以这样做:inline const std::string& name() const { return this->name; }

由于名称作为const引用返回,因此在类外部不会被修改,同时通过返回名称不会创建任何副本。

之后,如果你想操作名称,就必须进行复制。


谢谢你的回答!很有道理,但我考虑了一些在函数内计算名称的情况,例如 'fullname()' 函数,它返回姓和名粘在一起的完整姓名。 - tonytony
2
是的,在这种情况下,您将不得不返回该值,因为在从函数返回后,对本地变量的引用将无效。解决您问题的另一种方法是每次修改名字或姓氏时计算全名(您可以在setNamesetFirstName方法中执行此操作),并将其存储在fullname字段中。这样,您就不必每次调用fullname()时重新计算全名,只需返回对fullname字段的const引用(如我的答案中所写)。 - Mesop

6
我会选择第一个。返回值优化和C++11将消除任何复制开销。

3
优化的第一条规则:测量、优化、再测量。正如Knuth所说:“过早的优化是万恶之源”。
除非你有强烈的迹象表明仅返回std :: string将显着影响软件的性能,否则就这样做。如果您可以“测量”出重大影响,请找到关键路径并对其进行优化。不要进行任何有趣的项目范围内的“优化”,这可能导致代码的可读性、可维护性和鲁棒性受到负面影响。

2

由于我们有了移动语义(在C++11中),您可以使用以下内容:

string name();

即使在C ++03中,这也是几乎好的,因为编译器可以优化它(搜索返回值优化)。

1

我认为你应该使用第一种方法。因为这是一个简单的获取方法,而且这种获取/设置方法在各个地方都被使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接