0

I really need to understand how the memory is managed in C++ for the below cases:

Case-1:

string s1 = "ABCD";
s1= "EFGH";

What happens in the memory w.r.t the string value "ABCD"? Does it automatically get deallocated? Or is it a memory leak here?

Case-2:

char* val = "ABCD";
val = "EFGH";

Similar to Case-1, how is Case-2 different from Case-1?

Case-3:

string s1 = "ABCD";
string s2 = s1;

Is separate memory allocated to s2 with value "ABCD"? Or do s1 and s2 point to the same memory location here? How is memory managed in this case?

Please share a link to study in depth on these? Any suggestions will definitely help me.

Fred Larson
  • 56,061
  • 15
  • 106
  • 157
  • 2
    *Please share a link to study in depth on these?* -- None of those examples will leak memory. – PaulMcKenzie Feb 05 '20 at 21:43
  • *Or do s1 and s2 point to the same memory location here?* -- And what if `s2` changes values? Does `s1` change also? Is this what you're asking? – PaulMcKenzie Feb 05 '20 at 21:53
  • @PaulMcKenzie It sounds like it, and it isn't that strange. It's copy-on-write and that is how Qt's strings work. Edit : Turns out a bunch of Qt objects use it. – François Andrieux Feb 05 '20 at 21:57
  • An important reason for using `std::string` is that *you don't need to worry about memory management*. What actually happens is an implementation detail that isn't specified in the standard. The `char*` example is just reassigning a pointer from one static string to another, so there's no memory management to worry about there either. – Fred Larson Feb 05 '20 at 22:05
  • 1
    `char* val` needs to be `const char* val` – Alan Birtles Feb 05 '20 at 22:10

2 Answers2

1
  1. Modern std::string implementations roughly follows these rules(Well explained here too):

    • Short Strings(<=26 chars?) are allocated on the stack using an optimization called SSO.
    • Strings longer than than are allocated on the heap. The std::string implementation internally manages both the allocations and deallocations here.
  2. const char* x = "ABCD" is allocated on the stack in static storage and a pointer to it is stored on the stack. This can be seen in the CE link here, where rsp:rsp-8 is made to point to .LC0.

Given the above observations:

  • Case 1: Its allocating x on the stack and then copying the new value "EFGH" to s1 without allocating any more stack space. CE Link
  • Case 2: It makes val point to the static storage where "ABCD" is stored and then to where "EFGH" is. CE Link
  • Case 3: This will(with no optimizations enabled) allocate 2 std::strings on the stack(CE Link), following from Case 1.

As noted in the comments, alternative std::string implementations are possible for example (1) Those that don't implement SSO (2) Implement copy-on-write.

tangy
  • 2,330
  • 1
  • 16
  • 34
  • 1
    I've rewritten parts of the answer to reflect your comments @walnut – tangy Feb 05 '20 at 22:39
  • 1
    As noted in the other answer, since C++11, copy-on-write implementations aren't really possible anymore, which is what I wasn't sure about on the spot when I wrote my previous comment. See https://stackoverflow.com/questions/12199710/legality-of-cow-stdstring-implementation-in-c11. – walnut Feb 05 '20 at 23:43
1

Case 1

string s1 = "ABCD";

string handles memory allocation to store "ABCD" for you. The string destructor will be called automatically when s1 goes out of scope which will free memory allocated by the string.

s1 = "EFGH";

A string copy assignment operator will overwrite the memory storing "ABCD" with "EFGH".

Case 2

char* val = "ABCD";
val = "EFGH";

Fred Larson answered this question in the comments above, you're just reassigning a pointer to a static string. The stack will allocate memory only for the pointer (not the data), and the pointer will be automatically deallocated when val goes out of scope.

Case 3

string s1 = "ABCD";
string s2 = s1;

s2 will allocate memory and copy s1. A copy on write implementation of the copy assignment operator would only allocate and copy memory if s2 is mutated. However, as of C++11 it seems that COW implementations of std::string are no longer allowed (Legality of COW std::string implementation in C++11).

Case 4

Here is an example of a scenario where you do need to worry about freeing allocated memory.

You might use this if you need a string to outlive the scope in which it was created. (In practice, you should usually avoid this approach. See Case 5 below.)

string* s1 = new string("ABCD");
delete s1;

In this example string is still internally managing memory to store "ABCD", but the string pointer is heap allocated and the string destructor will not be called when s1 goes out of scope. In this case you are responsible for using delete to ensure the memory is cleaned up when s1 is no longer required.

Case 5

shared_ptr<string> s1 = make_shared<string>("EFGH");

shared_ptr is usually the way to go versus new and delete. Shared pointers prevent a lot of memory leaks due to programming mistakes. shared_ptr handles delete for you, and uses reference counting to keep the string alive until the last reference has been destroyed.

Community
  • 1
  • 1
bcal
  • 139
  • 2
  • 1
    In most cases, I would say, `std::unique_ptr` is preferred over `std::shared_ptr`. The latter only makes sense if the object ownership is actually going to be shared. – walnut Feb 05 '20 at 23:49