Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to force `std::make_shared` to use a class's new operator?

Tags:

c++

c++11

c++14

Is it possible to force std::make_shared to use a class's new operator? This relates to another SO question. According to that question, std::make_shared uses a custom allocator:

From the standard (§20.7.2.2.6 shared_ptr creation ):

Effects: Allocates memory suitable for an object of type T and constructs an object in that memory via the placement new expression ::new (pv) T(std::forward(args)...).

As such, I thought that I could use a custom placement new operator, but that appears to be false

// std::cout
#include <iostream>

// std::make_shared
#include <memory>

// Track what we're making
struct Foo {
    Foo() {
        std::cout << "Foo constructor" << std::endl;
    }
    Foo(Foo const & foo) {
        std::cout << "Foo copy constructor" << std::endl;
    }
    Foo(Foo && foo) {
        std::cout << "Foo move constructor" << std::endl;
    }
    Foo & operator = (Foo const & foo) {
        std::cout << "Foo copy assignment" << std::endl;
        return *this;
    }
    Foo & operator = (Foo && foo) {
        std::cout << "Foo move assignment" << std::endl;
        return *this;
    }
    void * operator new(std::size_t size) throw(std::bad_alloc) {
        std::cout << "Foo new" << std::endl;
        return ::operator new(size);
    }
    void * operator new(std::size_t size, void * p) throw() {
        std::cout << "Foo placement new" << std::endl;
        return ::operator new(size,p);
    }
    void* operator new (std::size_t size, std::nothrow_t const & nothrow_value) 
        throw()
    {
        std::cout << "Foo nonthrowing new" << std::endl;
        return ::operator new(size,nothrow_value);

    }
    void operator delete(void * p, std::size_t size) {
        std::cout << "Foo delete" << std::endl;
        ::operator delete(p);
    }
    ~Foo() {
        std::cout << "Foo destructor" << std::endl;
    }
};

int main() {
    std::cout << "---Creating foo" << std::endl;
    auto foo = std::make_shared <Foo> ();
    std::cout << "---Creating foo2" << std::endl;
    auto foo2 = std::shared_ptr <Foo> (new Foo());
    std::cout << "---Creating foo3" << std::endl;
    auto foo3 = std::allocate_shared <Foo> (std::allocator <Foo>());
    std::cout << "---fin" << std::endl;
}

which gives

---Creating foo
Foo constructor
---Creating foo2
Foo new
Foo constructor
---Creating foo3
Foo constructor
---fin
Foo destructor
Foo destructor
Foo delete
Foo destructor

Also tucked in there was an attempt to force an allocator that would call the custom new operator with the call to std::allocate_shared. In any case, is there a way to make std::make_shared call the custom new operators without defining a whole new allocator?

like image 772
wyer33 Avatar asked Jun 09 '16 19:06

wyer33


2 Answers

No, this is not possible (with make_shared).

Since a custom allocator for class T will typically be written and optimized (for example with a pool) to expect allocations of size T, and make_shared will be allocating more memory than that, I imagine it wasn't deemed an important feature to support.

Further, the standard provides allocate_shared for the case where you wish to use a custom allocator.

like image 84
Mark B Avatar answered Nov 15 '22 17:11

Mark B


Sort of.

What you do instead is create a structure containing aligned storage with a custom destroyer that cleans up the object and a bool saying if you should do so.

Then placement new the aligned storage.

Now use the aliasing shared ptr constructor to return a pointer to the aligned storage as a different type.

like image 44
Yakk - Adam Nevraumont Avatar answered Nov 15 '22 15:11

Yakk - Adam Nevraumont