Anne Quinn
Anne Quinn

Reputation: 13002

How to use shared_ptr to manage an object placed with placement new?

A fairly common thing I need to do is allot an object and some memory it'd like, in a strictly contagious region of memory together:

class Thing{
    static_assert(alignof(Thing) == alignof(uint32), "party's over");
public:
   
    ~Thing(){
        //// if only, but this would result in the equivalent of `free(danglingPtr)` being called
        //// as the second stage of shared_ptr calling `delete this->get()`, which can't be skipped I believe? 
        // delete [] (char*)this;
    }

    static Thing * create(uint32 count) {
        uint32 size = sizeof(Thing) + sizeof(uint32) * count; // no alignment concerns
        char * data = new char[size];
        return new (data)Thing(count);
    }
    
    static void destroy(Thing *& p) {
        delete [] (char*)p;
        p = NULL;
    }

    uint32 & operator[](uint32 index) {
        assert(index < m_count);
        return ((uint32*)((char*)(this + sizeof(Thing))))[index];
    }

private:
    Thing(uint32 count) : m_count(count) {};
    uint32 m_count;
};

int main(){
    {
        auto p = shared_ptr<Thing>(Thing::create(1));
        // how can I tell p how to kill the Thing?
    }
    return 0;
}

In Thing::Create() this is done with placement new into a section of memory.

I'd also like to have a shared pointer manage it in this case, using auto p = shared_ptr<Thing>(Thing::create(1)). But If it calls the equivalent of delete p.get() when the ref count empties, that'd be undefined as it mismatches the type and, more importantly, mismatches plural new with singular delete. I need it to delete in a special way.

Is there a way to easily set that up without defining an outside function? Perhaps by having the shared pointer call Thing::destroy() when the ref count empties? I know that shared pointer can accept a "deleter" as a template argument, but I'm unsure how to use it, or if it's even the proper way to address this?

Upvotes: 1

Views: 346

Answers (1)

Miles Budnek
Miles Budnek

Reputation: 30494

std::shared_ptr accepts a deleter function as a second parameter, so you can use that to define how the managed object will be destroyed.

Here's a simplified example:

class Thing
{
public:
    ~Thing()
    {
        std::cout << "~Thing\n";
    }

    static std::shared_ptr<Thing> create() {
        char * data = new char[sizeof(Thing)];
        Thing* thing = new (data) Thing{};
        return std::shared_ptr<Thing>{thing, &Thing::destroy};
    }
    
    static void destroy(Thing* p) {
        p->~Thing();
        delete [] (char*)p;
    }
};

int main()
{
    auto p = Thing::create();
}

Live Demo

Upvotes: 2

Related Questions