6

I was playing with the new and delete operators overloading when I noticed something strange.

  1. I have:

    void* operator new(size_t size)
    {
        std::cout << "Allocating memory..." << std::endl;
    
        void* p = malloc(size);
    
        if (NULL == p)
        {
            throw std::bad_alloc();
        }
    
        return p;
    }
    
  2. When I do:

    int main()
    {
        int* x = new int(1);
        std::cout << *x << std::endl;
        delete x;
        return EXIT_SUCCESS;
    }
    

    Everything works as expected and I get:

    Allocating memory...
    1
    
  3. But when I do:

    int main()
    {
        std::string* s = new std::string("Hello world");
        std::cout << *s << std::endl;
        delete s;
        return EXIT_SUCCESS;
    }
    

    I get:

    Allocating memory...
    Allocating memory...
    Hello world
    
  4. In fact, when I do:

    int main()
    {
        std::string s = "Hello world";
        return EXIT_SUCCESS;
    }
    

    I still get Allocating memory...!

  5. Finally, I do:

    int main()
    {
        std::string s = "Hello world";
        std::cout << &s << std::endl;
        while (true);
    }
    

    To get something like:

    $ ./test &
    [1] 8979
    Allocating memory...
    0xbfc39a68
    $ cat /proc/8979/maps | grep stack
    bfc27000-bfc3c000 ... [stack]
    

    So now I'm sure the s variable is allocated on the stack... but then, what's calling the new operator? My best guess would be it has something to do with the memory allocation for the actual literal, "Hello world"... but it's supposed to be static memory, and new is all about dynamic memory.

What's going on?

Update

After reading the comments and debugging the example myself I wanted to conclude that indeed, once the string constructor is called, it allocates memory on the heap for its internal implementation. This can be seen by tracing the new call:

(gdb) b 13 // that's the std::cout << "Allocating memory..." << std::endl; line
(gdb) r
... Breakpoing 1, operator new (size=16) at test.cpp:13 ...
(gdb) backtrace
#0 operator new (size=16) at main.cpp:13
#1 std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) () from /usr/lib/libstdc++.so.6
...

And reading the std::string (well, basic_string.tcc) source code:

template<typename _CharT, typename _Traits, typename _Alloc>
typename basic_string<_CharT, _Traits, _Alloc>::_Rep*
basic_string<_CharT, _Traits, _Alloc>::_Rep::
_S_create(size_type __capacity, size_type __old_capacity,
          const _Alloc& __alloc)
{

   ...

    void* __place = _Raw_bytes_alloc(__alloc).allocate(__size);
   _Rep *__p = new (__place) _Rep; // Bingo!
   __p->_M_capacity = __capacity;

   ...

}

So yeah. Programming's cool.

5
  • note: malloc/free and new/delete - are pair Commented Jul 23, 2013 at 12:35
  • 1
    Why don't you use a debugger and break in your operator new to see who is calling it? Streams do allocate memory, too. Commented Jul 23, 2013 at 12:36
  • 2
    The whole point of std::string is that it wraps up a char* and own/manages it for you. So when you create a std::string, it allocates some memory to store the actual characters. The constructor can even take a custom allocator. Commented Jul 23, 2013 at 12:37
  • @GrijeshChauhan: I also implemented delete via free, if that's what you mean, but it was irrelevant to the question, so I omitted it. Commented Jul 23, 2013 at 12:37
  • It's not the literal, the std::string class is performing memory allocation internally. The details aren't particularly important, but is a wrapper around C-style strings (nul-terminated arrays of char) that manages memory allocation for you. Commented Jul 23, 2013 at 12:51

5 Answers 5

5

When you write

std::string s = "Hello world";

you are calling the constructor string (const char* s);, the specification of this constructor is Copies the null-terminated character sequence (C-string) pointed by s. So the constructor allocates memory to store the copy.

Sign up to request clarification or add additional context in comments.

1 Comment

The you in the last sentence is confusing, it would better be phrased So s need to allocate the memory to store the copy but then I would advise you change the constructor parameter name since it is also s.
3

Example 3.: (first note, that you have to ust std::string *s instead of std::string s, since new returns a pointer).

Using gdb i set a breakpoint at the std::cout line and backtraced the two calls:

The first one is the new you wrote in your code. The second one happens to be called from within libstdc++.so.6 (in my case):

(gdb) backtrace
#0  operator new (size=36) at test.cpp:6
#1  0x00007ffff7b78a89 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00007ffff7b7a495 in char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7b7a5e3 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x0000000000400da1 in main () at test.cpp:20

Comments

1
  std::string s = "Hello world";

Right hand side is static memory s constructor allocates new memory and copies "Hello world",

Comments

0

std::string is a structure that encapsulates a c-styled string.

A first allocation is made for the string container-structure.
A second allocation is for a copy of the string which is made in the string construction.

Comments

0

The string object itself allocates space for the actual characters using new. This is done in the string's constructor.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.