如何将std::string传递给函数?

17

如果一个函数内部只是复制字符串,那么我是否应该总是将std::string以const引用的形式传递给函数?另外,按值传递和按引用传递之间有什么区别(性能或其他方面)?据我所知,一个使用operator=,另一个使用复制构造函数。是这样吗?


4
当你通过引用传递时,不会进行复制。当你通过值传递时,会使用复制构造函数。 - Anycorn
2个回答

31

不要轻信互联网上的一切。最好使用const引用进行传递。为了证明这一点,我编写了一个测试程序...

test.cpp:

#include <ctime>
#include <iostream>
#include <string>

void foo(std::string s);
void bar(const std::string& s);

int main() {
    const std::string s("test string");

    clock_t start = clock();
    for (int it = 0; it < 1000000; ++it)
        foo(s);
    std::cout << "foo took " << (clock() - start) << " cycles" << std::endl;

    start = clock();
    for (int it = 0; it < 1000000; ++it)
        bar(s);
    std::cout << "bar took " << (clock() - start) << " cycles" << std::endl;
}

aux.cpp:

#include <string>
std::string mystring;

void foo(std::string s) { mystring = s; }
void bar(const std::string& s) { mystring = s; }

使用'g++ -O3 test.cpp aux.cpp'编译并得到输出:

foo took 93044 cycles
bar took 10245 cycles

按引用传递速度比按值传递快一个数量级。


1
在Visual Studio 2012中: "foo花费了3701个周期 bar花费了1110个周期" - nergeia
使用x86_64上的g++ 4.8.1,我得到了4130与1820。差距正在缩小。 - John Kugelman
1
使用Clang 602,我看到22500与17000(大约,一致)。差距已经几乎缩小到了。 - Alex Celeste
4
这个测试很误导人,因为它比较的是制作两份副本与制作一份副本之间的差异(显然,两份副本更慢)。有关按值传递的真正主张是,如果我们要复制参数,我们应该按值传递,然后将参数移动到目标中。所以foo的主体应该像这样:mystring = ::std::move(s)。我认为您会发现在这种情况下结果大不相同。 - Ken Wayne VanderLinde
2
所以我尝试了Ken的想法。const ref:约12000个周期。两个复制:16000个周期。复制+移动:约12000个周期。然后我尝试了一个长字符串,得到了这个结果:const ref:约12000个周期。两个复制:80000个周期。复制+移动:约80000个周期。const ref似乎始终更有效率。 - Guillaume Gris
1
@GuillaumeGris 请原谅我的怀疑,但这些数字听起来不正确。您确定您执行的是复制后跟随移动的操作吗?我会自己运行测试以确保,但它似乎不太对劲。复制+移动情况应该只是相对于const ref情况的额外指针交换,我无法看到它会导致500-600%的开销。 - Ken Wayne VanderLinde

22

如果函数内部只是复制字符串,我是否应该始终通过const引用传递std :: string?

不需要。如果您只是在函数内部复制字符串,则应该按值传递。这允许编译器执行几个优化。要了解更多信息,请阅读Dave Abraham的“想要加速吗?按值传递。”

按值传递和按引用传递之间有什么区别(性能或其他方面)?据我所知,一个使用operator =,另一个使用复制构造函数。是这样吗?

不是这样的。引用不是对象;它是对对象的引用。当按值传递时,将复制要传递的对象。当按引用传递时,将创建对现有对象的引用,并且不会进行复制。一本好的C ++入门书将详细解释这些基本概念。如果您想在C ++中开发软件,则理解基础知识至关重要。


我认为这取决于他所说的复制是什么意思。如果是针对成员变量,那么应该将其作为引用传递并复制到成员变量中。在C++0x中,可以按值传递并使用std::move将其移动到成员变量中。 - GManNickG
你可以使用swap方法(或std::swap)来获得类似的行为,而不是使用std :: move,当你不能/不使用c++0x。 - Grizzly
有没有编译器和示例代码的组合,其中按值传递实际上表现更好?我想知道。 - Johan Kotlinski

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