提供以下类型
// 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);
};
以下构造函数是将Image
转换为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
用作“值类型”,它按值传递,而Image
仅在shared_ptrs中传递。
答案 0 :(得分:3)
下面的构造函数是理智的。
由于使用了析构函数,因此可以延长Image
的生存期,因此data
仍然有效。
所以您在这一点上是正确的...
但是,向量可能会重新分配,从而使缓冲区无效。
因此生成的代码不安全。
为了安全起见,您可以存储std::shared_ptr<std::vector<uint8_t>> mData;
。
..正确的方法
我们使用std::shared_ptr
的别名构造器更好/更简单:
struct PtrImage
{
std::shared_ptr<std::vector<uint8_t>> mData;
PtrImage (std::shared_ptr<Image> pIm) : mData(pIm, &pIm->GetData()) {}
};
因此所有权信息PtrImage::mData
与pIm
共享。
注意:我假设GetData()
返回的向量具有与Image
相同(或更长)的寿命(与VecImage
一样)。如果它是不相关的向量(来自其他对象),那么您将没有解决方案。
如评论中所述,vector不应重新分配
答案 1 :(得分:0)
对我来说很危险:
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!
现在p 会发生什么?共享指针包含一个指针,该指针指向重新分配之前驻留在向量中的数据。但是此数据已被替换并被删除。因此,您现在有一个悬空的指针存储在p中(在uint8_t
指针中),使用它(最迟在您的智能指针尝试删除其数据时发生)将导致不确定的行为。
答案 2 :(得分:0)
您甚至不应尝试让共享指针猜测它是否应删除其对象。这样一来,您一次就会被一个极端的案例所困扰,即使您设法找到全部,也可能会被一个显然无关的更改所创建的新案例所吸引。
如果您需要将Image
转换为PtrImage
,只需说要从T生成shared_ptr<T>
。有两种标准方法:复制或移动,并且shared_ptr
拥有其对象的所有权。
在您的示例中,由于Image
仅返回左值引用,因此只能使用副本。您可以通过拥有VecImage
成员的所有权而离开data
。