Reputation: 1336
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.
Upvotes: 2
Views: 130
Reputation: 25526
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.
Upvotes: 1
Reputation: 217283
is the following constructor a sane..
You extend lifetime of Image
thanks to destructor, so data
is still valid.
So you are correct on that point...
But, vector may reallocate, invalidating the buffer.
So resulting code is unsafe.
You could store std::shared_ptr<std::vector<uint8_t>> mData;
to be safe.
.. and correct way
We have better/simpler with aliasing constructor of std::shared_ptr
:
struct PtrImage
{
std::shared_ptr<std::vector<uint8_t>> mData;
PtrImage (std::shared_ptr<Image> pIm) : mData(pIm, &pIm->GetData()) {}
};
So ownership information PtrImage::mData
is shared with pIm
.
Note: I assumes that vector returned by GetData()
has same (or longer) lifetime that Image
(as for VecImage
). if it is an unrelated vector (from other object), then you won't have solution.
As noted in comment, vector should not reallocate neither
Upvotes: 3
Reputation: 148910
You should not even try to have a shared pointer guess whether it should delete its object or not. It you proceed that way, you will be caught at a time by a corner case or even if you managed to find all, by a new one created by an apparently unrelated change.
If you need to convert an Image
to a PtrImage
just say that you want to build a shared_ptr<T>
from a T. There are 2 standard ways: a copy or a move, and in either the shared_ptr
has ownership of its object.
In your example, as Image
only returns a lvalue reference, you can only use a copy. You could have a move from a VecImage
by taking ownership of its data
member.
Upvotes: 0