Search code examples
c++shared-ptrvalue-type

Can you use a member of deleter to keep alive a shared_ptr?


Given the following types

// interface and implementation used in one part of the codebase
struct Image
{
    virtual std::vector<uint8_t>& GetData () = 0;
};

struct VecImage : public Image
{
    std::vector<uint8_t> mData;

    std::vector<uint8_t>& GetData () { return mData; }
};

// used in another part of the codebase
struct PtrImage
{
    std::shared_ptr<uint8_t> mData;

    PtrImage (std::shared_ptr<Image> pIm);
};

is the following constructor a sane and correct way to convert an Image to a PtrImage?

PtrImage::PtrImage (std::shared_ptr<Image> pIm)
{
    struct im_deleter
    {
        std::shared_ptr<Image> keepAlive;
        void operator () (uint8_t* ptr)
        {
            keepAlive.reset ();
        }
    };

    mData = { &pIm->GetData()[0], im_deleter { pIm } };
}

PtrImage is used as a "value type", it is being passed around by value, while Image is passed around in shared_ptrs only.


Solution

  • Looks pretty dangerous to me:

    std::shared_ptr<Image> i = std::make_shared<VecImage>(/* some data */);
    PtrImage p(i); // has now stored a pointer to the vector's data
    i->getData()->push_back(0); // repeat until re-allocation occurs!
    

    What would p now hold? The shared pointer holds a pointer to the data that resided in the vector before re-allocation; but this data was replaced and got deleted. So you now have a dangling pointer stored in p (in the uint8_t pointer), using it (which will happen at latest when your smart pointer tries to delete its data) will result in undefined behaviour.