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.
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.