Sunday, September 15, 2019

C++11 std::make_shared does not use class overloaded new/delete operators

Even if std::unique_ptr, std::make_unique and std::shared_ptr use the class overloaded new/delete operators, std::make_shared does not.

Below is a pseudo-code highlighting the behavior:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <memory>
#include <iostream>

class C
{
public:
    void * operator new(size_t size)
    {
        void * p = ::operator new(size);
        std::cout << "C::new() -> " << p << "\n";
        return p;
    }

    void operator delete(void * p)
    {
        std::cout << "C::delete() -> " << p << "\n";
        ::operator delete(p);
    }
};

std::shared_ptr<C> ptr = std::make_shared<C>();

Explanations:

The std::make_shared documentation clearly explains that the creation is using the global new operator. The second aspect is that the allocation creates only a single buffer (i.e. one heap allocation) where the size is the class size plus the control block for the 'shared control' as opposed to the std::shared_ptr which creates two distinct memory buffers.

Below is a pseudo-code better highlighting the behavior:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <memory>
#include <iostream>

void * operator new(size_t size)
{
    void * p = malloc(size);
    std::cout << "Global::new() -> " << p << ", size=" << size << "\n";
    return p;
}

void operator delete(void * p)
{
    std::cout << "Global::delete() -> " << p << "\n";
    free(p);
}

class C
{
public:
    void * operator new(size_t size)
    {
        void * p = ::operator new(size);
        std::cout << "Global::new() -> " << p << ", size=" << size << "\n";
        return p;
    }

    void operator delete(void * p)
    {
        std::cout << "C::delete() -> " << p << "\n";
        ::operator delete(p);
    }
};

int main()
{
    std::shared_ptr<C> ptr = std::make_shared<C>();
}

Use C++shell to run the program.


1
2
Global::new() -> 0x3da04e0, size=24
Global::delete() -> 0x3da04e0


std::make_ptr side-effect:

When reading this post, I discovered that a std::weak_ptr could keep the control block alive and so, keeping the complete allocation alive even if std::shread_ptr count is 0.

https://stackoverflow.com/questions/20895648/difference-in-make-shared-and-normal-shared-ptr-in-c?rq=1


References:

Have a look to few question/answers on that topic:
https://stackoverflow.com/questions/57929127/why-stdmake-shared-is-always-using-the-global-memory-allocation-even-with-cl
https://stackoverflow.com/questions/49423512/c-make-shared-when-custom-new-and-delete-operators-are-defined?rq=1

Here is the explanation:
https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared

Here is the technicality


[keywords: std::make_shared, std::shared_ptr, c++11, c++14]

No comments:

Post a Comment