3

I am trying to create a simple memory manager to get familiar with the concept, I have provided overrides for global new and delete and just started messing with constructing some objects when I noticed that for a single dynamic string allocation, I seem to hit new twice. The first time new gets hit, its even before the string constructor has been called and strangely, this is the larger of the two allocations, the second time it gets hit, the call comes from the std::string (basic_string) constructor.

I would like to know what the two new's are about. Specifically, I care about it in this context because every one of those new's creates its own allocation header and I have an academic curiosity in the kind of overhead I would be introducing if I used a simple memory manager.

Relevant code :

class DumbDataType
{
    std::string m_name;
};

int main()
{
    printf("STARTING MEMORY MANAGEMENT TESTING \n");

    printf("******************* Creating DATA ******************* \n");
    std::string* data = new std::string();
    delete data;

    printf("******************* Creating LORE ******************* \n");
    DumbDataType * lore = new DumbDataType();
    delete lore;

    getchar();
}

Output when I run this

STARTING MEMORY MANAGEMENT TESTING 
******************* Creating DATA ******************* 
[New] 28 
[New] 8 
[Delete] 00D88C18 
[Delete] 00D88BC8 
******************* Creating LORE *******************
[New] 28 
[New] 8 
[Delete] 00D88C18 
[Delete] 00D88BC8 

operators new and delete

void * operator new(std::size_t size, MemoryManagement::Heap* heap)
{
    assert(heap != nullptr);
    assert(size > 0);
    return heap->Allocate(size);
}

void * operator new(std::size_t size)
{
    printf("[New] %i \n", size);
    return operator new (size, MemoryManagement::HeapFactory::GetDefaultHeap());
}

void operator delete (void * memoryLocation)
{
    if (memoryLocation != NULL)
    {
        printf("[Delete] %p \n", memoryLocation);
        MemoryManagement::Heap::Deallocate(memoryLocation);
    }
}

The 'GetDefaultHeap' method just gets the first element in a statically allocated array of heaps.

Allocate mallocs enough memory for the size and a header and returns the proper starting address after offsetting for the header. Deallocate subtracts the header offset from the memory address it gets and frees that memory (I can post these methods if they help, it just seemed like too much code)

Melebius
  • 6,183
  • 4
  • 39
  • 52
Vidrohi
  • 112
  • 10
  • 4
    Have you looked at the constructor of `std::string` to see if it allocates? – Sebastian Redl Nov 29 '17 at 09:05
  • @SebastianRedl Yup, I did. As I said, the first new happens even before it goes into that constructor , the second one happens during the course of that constructor being executed. – Vidrohi Nov 29 '17 at 09:07
  • I mean, have you looked at its source code? The first `new` is obviously the one you wrote in your own code. There's probably a second `new` inside the constructor (probably hidden behind an allocator), which you could discover simply by reading the source. – Sebastian Redl Nov 29 '17 at 09:10
  • Yes, I did look at its source code , the allocation happens in a file called xmemory0. So yes, there are definitely two calls to new one in my code one in the constructor. – Vidrohi Nov 29 '17 at 09:17
  • 1
    So there's two `new`s in the source and two calls to your `operator new`. Now what's the question? – Sebastian Redl Nov 29 '17 at 09:19
  • Please do not post images of text. https://www.howtogeek.com/howto/windows-vista/copy-to-the-clipboard-from-the-windows-command-prompt/ And the question title looks fine to me. – Melebius Nov 29 '17 at 09:26
  • 1
    @SebastianRedl, the OP asks why per single `new` in source there are two `new` calls actually happens. – Alex Nov 29 '17 at 09:31
  • 4
    Possible duplicate of [Where is a std::string allocated in memory?](https://stackoverflow.com/questions/42049778/where-is-a-stdstring-allocated-in-memory) – Alex Nov 29 '17 at 09:33

2 Answers2

6
new std::string()

This needs two allocations: one for the std::string object, another for its underlying memory buffer.

If you wrote

std::string s;

you'll see it calls new once.

YSC
  • 38,212
  • 9
  • 96
  • 149
  • 1
    Interesting, is there a place I can read more about this, object and buffer allocation ? – Vidrohi Nov 29 '17 at 09:08
  • 'std::string s;' That would be a stack construction right ? Does that just put the string object and buffer in one contiguous allocation ? – Vidrohi Nov 29 '17 at 09:09
  • @Vidrohi It will put the string object itself on the stack (likely a pointer to a buffer, the size of the buffer, and the number of bytes of data in the buffer), but it almost certainly won't try to put the buffer in one contiguous allocation because that would require all kinds of special cases throughout the `std::string` code since it can't be resized or freed. – David Schwartz Nov 29 '17 at 09:10
  • 5
    @Vidrohi Are you saying you are trying to write a memory allocator without even fully understanding the C++ object and memory models first? – Sebastian Redl Nov 29 '17 at 09:11
  • @DavidSchwartz Cool cool, I am curious then , how does stack allocation differ, as in, where does a stack allocated string store the actual string and the string object itself ? Or does this mean that in the stack allocation case the string object does not ?exist? – Vidrohi Nov 29 '17 at 09:12
  • Thanks for the information @YSC , I am looking into more information about the C++ memory model , but google seems to yield poor results on that query. I will mark the answer as correct as soon as I can read more about it and understand why your answer is correct. – Vidrohi Nov 29 '17 at 09:25
  • @Vidrohi As I explained, it will put the string object itself on the stack -- probably a pointer to the buffer, the size of the buffer, and the number of bytes of data in the buffer. It will allocate the buffer using its normal allocator. – David Schwartz Nov 29 '17 at 10:06
  • All objects are bundles of bytes with operations. `std::string::string` behaves the same way if it is constructing at a pointer that `new` returned or a pointer that is an offset to "the stack pointer". To `std::string::string`, those are just two pointers – Caleth Nov 29 '17 at 10:09
1

As @YSC pointed out, here is why there are two calls to new. The first new allocates the std::string object and returns a pointer to it, but that std::string object also has a pointer inside it and when it gets constructed it also calls new.

class DumberDataType
{
public:
    DumberDataType()
    {
        i = new int;
    }

private:
    int * i;

};

class DumbDataType
{
    DumberDataType ddt;
};

int main()
{
    printf("STARTING MEMORY MANAGEMENT TESTING \n");

    printf("******************* Creating DATA ******************* \n");
    DumbDataType* data = new DumbDataType();

    getchar();
}

The output of this code is

STARTING MEMORY MANAGEMENT TESTING
******************* Creating DATA *******************
[New] 4
[New] 4
Melebius
  • 6,183
  • 4
  • 39
  • 52
Vidrohi
  • 112
  • 10