通过引用和复制调用函数的 C++

C++ call to function by reference and copy

提问人:Maxou 提问时间:6/7/2023 最后编辑:wohlstadMaxou 更新时间:6/7/2023 访问量:111

问:

void    ref(std::string& str)
{ (void)str; }
void    copy(std::string str)
{ (void)str; }

int main()
{
    std::string str = "Hello World";
    for(size_t i = 0; i < 10000; i++)
        ref(str);
}

为什么当我调用 10000 次或 .ref(str)copy(str)

据我了解,复制一个对象应该为一个新对象分配新的空间。

如何使不重新分配此位置,并且在函数调用中仍然有一个复制对象。std::string

10000 次调用复制的 valgrind 输出

==19794== HEAP SUMMARY:
==19794==     in use at exit: 0 bytes in 0 blocks
==19794==   total heap usage: 1 allocs, 1 frees, 72,704 bytes allocated

为什么我没有 10000 个分配?

对 ref 的 10000 次调用的 valgrind 输出

==19794== HEAP SUMMARY:
==19794==     in use at exit: 0 bytes in 0 blocks
==19794==   total heap usage: 1 allocs, 1 frees, 72,704 bytes allocated
C++ 复制构造函数 stdstring

评论

0赞 user4581301 6/7/2023
如果编译器能找到一种方法来避免制作副本,它通常会接受它。还要注意,编译器不会优化所有内容,因为没有可观察到的效果。
1赞 273K 6/7/2023
total heap usage: 1 allocs, 1 frees- 没有 1000 个分配。
0赞 Maxou 6/7/2023
但是如果我通过复制给它,为什么没有 10000 个分配呢?即使我在复制函数中修改字符串: void copy(std::string str) { str = “random”; } 像这样它不会分配更多的内存,这怎么可能?
0赞 273K 6/7/2023
编译器可能会优化它,因为未使用。小字符串优化可能在业务中。str
0赞 François Andrieux 6/7/2023
该程序没有可观察的行为,因此可执行文件可以优化为任何内容,包括空程序。带有 -O3 的 gcc 13 完全消除了循环:godbolt.org/z/fr6611MT5for

答:

3赞 KamilCuk 6/7/2023 #1

因为您的字符串足够小,可以适应小字符串优化。

3赞 Md. Faisal Habib 6/7/2023 #2

您找到的结果可能因编译器及其优化设置而异。

大多数现代编译器都在此处应用优化。

调用 copy 函数时,编译器会应用复制省略并优化对象的副本。编译器不会为复制的对象分配新的内存,而是直接重用现有对象,从而消除了额外分配的需要。std::stringstd::string

从此处了解有关复制省略的更多信息

什么是复制省略和返回值优化?

-2赞 hleme 6/7/2023 #3

在我最初的回答中,我说当子例程按值接收参数时,会进行浅层复制。@Blastfurnace在评论中提醒我,它实现了一个带有深度复制的构造函数。因此,应该进行 1000 次内存分配,并且由于编译器的内部优化,可能只发生一次内存分配。std::stringstd::string

评论

1赞 user4581301 6/7/2023
std::string 是一个 32 字节的结构当然可以,但实际大小没有在任何地方指定。过去我也见过 8 字节和 40 字节。答案的其余部分读起来就像是试图回答一个不同的问题。std::string
0赞 Blastfurnace 6/7/2023
您的第一段有严重错误。传递 by 值时,将复制对象和字符串内容。否则,传递者值将允许被调用函数修改调用方的变量。只有当参数通过引用传递时,这才有可能。此外,如果传递足够长的 by 值,它将为复制的字符串内容分配内存。std::stringstd::stringstd::stringstd::string
0赞 hleme 6/7/2023
当仅复制第一个对象且其引用的对象保持不变时,则存在浅拷贝。当复制第一个对象时,会有一个深拷贝,并且分层复制引用的对象也会被复制。您是说调用按值传递参数的子例程会进行深度复制。你错了,我只是想确认我的观点。按值将参数传递给子例程会生成浅拷贝。我使用的是带有 g++ 22.04 的 Ubuntu 11.3。
0赞 hleme 6/7/2023
@Blastfurnace,浅拷贝是C++的默认行为,但实现了深拷贝构造函数。所以因为有一个深拷贝。因此,对于 ,按值传递会分配内存。我会改变我的答案。std::stringstd::stringstd::string