在另一个函数中分配内存并使用共享指针

6

我在工作中有一些旧版代码,它接收一个双指针并为其分配内存。一个简化的示例看起来像这样:

struct LegacyObj
{
    int a;
    double b;
};

void LegacyAllocator(LegacyObj** ppObj)
{
    *ppObj = (LegacyObj*)malloc(sizeof(LegacyObj));
}

void LegacyDeleter(LegacyObj** ppObj)
{
    free(*ppObj);
}

实际的LegacyAllocator函数有大约100行代码,将文件读取和创建LegacyObj指针的链表混合在一起。目前来说我无法重写这个函数。然而,我希望能让使用这个函数更加安全,避免因为异常等情况导致内存泄漏。我想出的第一个解决方案是将其封装成一个类,在构造函数和析构函数中处理调用遗留函数。

class RAIIWrapper
{
public:
    RAIIWrapper()
        :obj{nullptr}
    {
        ::LegacyAllocator(&obj);
    }
    RAIIWrapper(RAIIWrapper&& that)
        : obj{ that.obj}
    {
        that.obj = nullptr;
    }
    RAIIWrapper& operator=(RAIIWrapper&& that)
    {
        RAIIWrapper copy{std::move(that)};
        std::swap(obj, copy.obj);
        return *this;
    }
    ~RAIIWrapper ()
    {
        ::LegacyDeleter(&obj);
    }

private:
    LegacyObj* obj;
};

但是我很好奇 - 是否有一种方法可以使用std::shared_ptrstd::unique_ptr来完成这个任务呢?我还没有找到一种解决方法,而又不需要保留传递给LegacyAllocator的原始指针。


unique_ptrshared_ptr不接受分配器,它们只接受删除器。您可以使用LegacyAllocator(&obj)自己分配指针,并为unique_ptr/shared_ptr创建一个删除器函数,该函数在其operator()中调用LegacyDeleter(&p) - David
3个回答

3

是的,您可以使用自定义删除器(custom deleter)与std::unique_ptrstd::shared_ptr一起使用,例如:

struct Deleter {
  void operator()(LegacyObj *p) const {
    LegacyDeleter(&p);
  }
};

std::unique_ptr<LegacyObj, Deleter> MakeLegacyObj() {
  LegacyObj *p = 0;
  LegacyAllocator(&p);
  return std::unique_ptr<LegacyObj, Deleter>(p);
}

std::unique_ptr<LegacyObj, Deleter> p = MakeLegacyObj();

正如 @Dave 正确指出的那样,这也适用于 shared_ptr

std::shared_ptr<LegacyObj> p = MakeLegacyObj();

1
std::shared_ptr<LegacyObj> p = MakeLegacyObj(); 也可以正常工作。 - David
谢谢,看起来创建一个自定义的删除器函数是我所缺少的。 - The Forest And The Trees

0

你可以使用unique_ptr来删除内存,但是你需要提供一个自定义的Deleter类,因为内存是使用malloc而不是new分配的。

更好的方法是,将分配代码更改为使用new,然后只需使用unique_ptr即可。如果你选择这条路,你可以让分配器返回一个unique_ptr而不是指向内存的指针。

假设你需要提供自己的自定义删除器,下面是一种可能的方式:

template <typename T>
class MallocDeleter
{
public:
  void operator() (T* obj) const
  {
    LegacyDeleter (*obj);
  }
};

typedef std::unique_ptr <LegacyObj, MallocDeleter <LegacyObj>> unique_legacy_ptr;

你也可以提供一个名为make_unique_legacy的函数,通过调用LegacyAllocator来分配内存,而不是自己初始化unique_ptr

0
您可以像这样为unique_ptr创建一个工厂函数:
typedef void(* LegacyDeleterType)(LegacyObj*);
typedef std::unique_ptr<LegacyObj,LegacyDeleterType> UniqueLegacyPtr;

UniqueLegacyPtr makeUniqueLegacyObj()
{
    LegacyObj * p = nullptr;
    LegacyAllocator( &p );
    return UniqueLegacyPtr( p, [](LegacyObj*p){ LegacyDeleter(&p); } );
}

现在您可以使用它来创建unique_ptr,并且还可以分配给shared_ptr,该指针在构造时自动捕获自定义删除器:

int main()
{
    auto unique = makeUniqueLegacyObj();
    std::shared_ptr<LegacyObj> shared = makeUniqueLegacyObj();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接