0
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);
}

Why do I get the same amount of allocations when I call 10000 times ref(str) or copy(str).

As far as I understand, making a copy of an object should allocate new space for a new object.

How Does std::string make to not reallocate this place and still have a copy object in a function call.

The output of valgrind for 10000 calls to copy

==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

Why I don't have 10000 allocation ?

The output of valgrind for 10000 calls to ref

==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
wohlstad
  • 12,661
  • 10
  • 26
  • 39
Maxou
  • 37
  • 5
  • If the compiler can find a way to avoid making a copy it'll generally take it. Also watch out that the compiler doesn't optimize everything out since there are no observable effects. – user4581301 Jun 06 '23 at 20:48
  • 1
    `total heap usage: 1 allocs, 1 frees` - there are no 1000 allocations. – 273K Jun 06 '23 at 20:48
  • But why there is no 10000 alloc if I give it by copy ? even if I modify the string in the copy function: void copy(std::string str) { str = "random"; } like this it doesnt allocate more memory, How is this possible ? – Maxou Jun 06 '23 at 20:54
  • The compiler might optimize it since `str` is not used. Small string optimization might be in business. – 273K Jun 06 '23 at 21:00
  • The program has no observable behaviour, so the executable could be optimized to anything up to and including an empty program. gcc 13 with -O3 completely removes the `for` loop : https://godbolt.org/z/fr6611MT5 – François Andrieux Jun 06 '23 at 21:17

3 Answers3

3

Because your string is small enough to fit into small string optimization.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
3

The result you found might vary based on the compiler and its optimization settings.

Most modern compilers apply optimization here.

When you're calling the copy function, the compiler applies copy elision and optimizes the copy of the std::string object. Instead of allocating new memory for the copied object, the compiler directly reuses the existing std::string object, eliminating the need for additional allocations.

Learn more about copy elision from here

What are copy elision and return value optimization?

Md. Faisal Habib
  • 1,014
  • 1
  • 6
  • 14
-2

In my original answer I said that std::string does shallow copy when the subroutine receives the parameters by value. @Blastfurnace reminded me in the comments that std::string implements a constructor with deep copy. Therefore, 1000 memory allocations should occur, and possibly, only a single memory allocation occurs due to the compiler's internal optimization.

hleme
  • 90
  • 3
  • 1
    *std::string is a 32-byte structure* It certainly could be, but the actual size isn't specified anywhere. In the past I've also seen 8 and 40 byte `std::string`. The rest of the answer reads like like it's trying to answer a different question. – user4581301 Jun 06 '23 at 21:54
  • Your first paragraph has a serious error. When passing a `std::string` by value the `std::string` object *and the string contents* are **copied**. Otherwise a `std::string` passed by value would allow the called function to modify the caller's variable. That's only possible when the parameter is passed by reference. In addition, if you pass a long enough `std::string` by value it will allocate memory for the copied string contents. – Blastfurnace Jun 07 '23 at 03:47
  • A shallow copy exists when only the first object is copied and its referenced objects remain the same. And there is a deep copy when the first object is copied and hierarchically the referenced objects are copied as well. You are saying that calling a subroutine passing parameters by value does a deep copy. You are wrong, I just tried to confirm my point. Passing a parameter to a subroutine by value makes a shallow copy. I'm on an Ubuntu 22.04 with g++ 11.3. – hleme Jun 07 '23 at 06:43
  • @Blastfurnace, shallow copy is the default behavior for C++, but `std::string` implements a deep copy constructor. So for `std::string` there is a deep copy. So, for `std::string`, passing by value allocates memory. I will change my answer. – hleme Jun 07 '23 at 07:19