6

我正在使用一个旧的开源库,具有以下(简化的)感兴趣的 API:

// some class that holds a raw pointer to memory on the heap
// DOES NOT delete it in its destructor
// DOES NOT do a "deep" copy when copied/assigned (i.e., after copying both objects
// will point to the same address)
class Point;

// function used to construct a point and allocate its data on the heap
Point AllocPoint();
// function used to release the memory of the point's data
void DeallocPoint(Point& p);

// Receives a pointer/c-array of Points, along with the number of points
// Doesn't own the memory
void Foo(Point* points, int npts);

在 C++11 中使用此 API 的最佳(最安全/最易读/最优雅)的方式是什么。我不能简单地使用vector<unique_ptr<Point, PointDeleter>>(我可以在哪里PointDeleter实现一个简单的自定义删除器),因为那样我将无法使用该功能Foo(期望Point*而不是unique_ptr<Point>*)。

谢谢

4

5 回答 5

4

我会将这个非 RAII 类 C API 包装在RAII构建块中,然后在 C++11 代码中使用它们。

例如:您可以定义一个RaiiPoint包装(非 RAII)Point类的类,并在其构造函数调用AllocPoint()中,在析构函数中DeallocPoint()。然后,您可以定义适当的复制构造函数和 copy operator=,或者只实现移动语义(使用移动构造函数和 move operator=),或者根据您的要求使包装类既可复制又可移动。

然后,您可以简单地将 astd::vector<RaiiPoint>与基于 RAII 的包装类一起使用。

(这是一种通用方法,当您想在现代 C++ 代码中使用 C 库时可以使用:您可以将“原始”C 库句柄和对象包装在安全 RAII 边界中,并在现代 C++ 中使用这些健壮的安全包装类代码。)

于 2013-06-17T10:46:08.170 回答
4

可以使用std::vector<Point>,调用Foo( &v[0], v.size() )。但是在这里管理内存可能很棘手,因为Point显然没有提供任何干净的副本和分配;分配器中的自定义删除器将为每个元素调用,即使它被复制。

如果向量实际上应该拥有这些点,那么您可以将它包装在一个更复杂的类中,该类调用AllocPoint每次插入(并插入结果)和DeallocPoint每次删除(以及销毁时向量中剩余的所有内容)。但是,此类不应允许Point(非 const operator[]、非 const 迭代器等)进行写访问,因为这将允许更改 中的任何指针 ,并丢失正常工作Point所需的内容。DeallocPoint据推测,还有其他用于操作的功能 Point;您必须安排这些通过包装器界面可用。

于 2013-06-17T10:46:22.240 回答
4

如果你真的想让它看起来不错,你可能不得不编写一组非常全面的包装器来完全隐藏库的 API - 有效地,用一个在外部以现代 C++ 方式运行的库来包装整个库并将所有的混乱隐藏在里面。

这不是一项令人愉快的任务,但如果你能正确地了解该库的行为,那么从长远来看,它应该会让你的生活变得更轻松。如果您不打算非常广泛地使用这个外部库,可能不值得。

于 2013-06-17T10:42:56.930 回答
1

“你”可以编写一个简单的包装器来释放内存:

struct PointVectorWrapper {
  vector<Point> points;
  ~PointVectorWrapper() {
    for (Point& p : points) {
      DeallocPoint(p);
    }
  }
  PointVectorWrapper& operator=(const PointVectorWrapper&) = delete;
  PointVectorWrapper(const PointVectorWrapper&) = delete;
};
// Now the usage is simple and safe:
PointVectorWrapper points;
// ... populate points ...
Foo(points.data(), points.size())

但这似乎有点“临时”。什么是更标准/可重复使用的解决方案?

于 2013-06-17T10:30:35.340 回答
1

您可以使用带有自定义分配器的标准向量,它在构造方法上调用 AllocPoint,在 destruct 方法上调用 DeallocPoint()。

template<typename T>
class CustomAllocator : public std::allocator<T>
{
  //Rebind and constructors
};

template<>
class CustomAllocator<Point> : public std::allocator<Point>
{
   //Rebind and constructors

   //For c++11
   void construct( pointer p )
   {
      new (p) Point();
      *p = AllocPoint();
   }

   void construct( pointer p, const_reference val )
   {
      construct(p);
      //copy member from val to point if neccessary 
   };   


   void destroy( pointer p )
   {
      DeallocPoint(*p);
      p->~Point();
   }
};

typedef std::vector<Point, CustomAllocator<Point> > PointVector;
于 2013-06-17T20:25:35.807 回答