您可以使用Deleter的成员来使shared_ptr保持活动状态吗?

时间:2019-05-16 08:31:12

标签: c++ shared-ptr value-type

提供以下类型

// 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中传递。

3 个答案:

答案 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::mDatapIm共享。

注意:我假设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