简体   繁体   English

用于C ++ 11中RAW指针的SMART向量?

[英]SMART vector for RAW pointers in C++11?

I'm using an old open-source library, with the following (simplified) API of interest: 我正在使用一个旧的开源库,其中包含以下(简化)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);

What's the best (safest/most readable/most elegant) way of using this API in C++11. 在C ++ 11中使用此API的最佳方式(最安全/最可读/最优雅)是什么。 I can't simply use vector<unique_ptr<Point, PointDeleter>> (where PointDeleter is a simple custom deleter I can implement), because then I will not be able to use the function Foo (which expects Point* and not unique_ptr<Point>* ). 我不能简单地使用vector<unique_ptr<Point, PointDeleter>> (其中PointDeleter是一个我可以实现的简单自定义删除器),因为那时我将无法使用函数Foo (它需要Point*而不是unique_ptr<Point>* )。

Thanks 谢谢

If you really want to make it look nice, you're probably going to have to write a set of really comprehensive wrappers which completely hide the library's API - effectively, wrap the entire library with one that behaves in a modern C++ way on the outside and hides all the mess inside. 如果你真的想让它看起来不错,你可能需要编写一组非常全面的包装器来完全隐藏库的API - 实际上,整个库用一个在外部以现代C ++方式运行的库包装并隐藏所有内部的混乱。

Not a pleasant task, but if you can get the behaviour of that library right then it should make your life a lot easier in the long term. 这不是一项令人愉快的任务,但是如果你能够获得该库的行为,那么它应该会让你的生活更加轻松。 Might not be worth it if you're not going to use this external library very extensively though. 如果您不打算非常广泛地使用这个外部库,那么可能不值得。

I would wrap this non-RAII C-like API in RAII building blocks, and then use them in C++11 code. 我会在RAII构建块中包装这个非RAII C类API,然后在C ++ 11代码中使用它们。

For example: you can define a RaiiPoint class that wraps the (non-RAII) Point class, and in its constructor calls AllocPoint() , in the destructor DeallocPoint() . 例如:你可以定义一个RaiiPoint类包装(非RAII) Point类,并在它的构造函数调用AllocPoint()在析构函数DeallocPoint() Then you can define proper copy constructor and copy operator= , or just implement move semantics (with move constructor and move operator= ), or make the wrapper class both copyable and movable, basing on your requirements. 然后,您可以定义正确的复制构造函数和复制operator= ,或者只是实现移动语义(使用移动构造函数和移动operator= ),或者根据您的要求使包装类既可复制又可移动。

Then you can simply use a std::vector<RaiiPoint> with your RAII-based wrapper class. 然后你可以简单地使用std::vector<RaiiPoint>和你的基于RAII的包装类。

(This is a general approach that you can use when you want to use C libraries in modern C++ code: you can wrap the "raw" C library handles and objects in safe RAII boundaries, and use these robust safe wrapper classes in your modern C++ code.) (这是您在现代C ++代码中使用C库时可以使用的一般方法:您可以将“原始”C库句柄和对象包装在安全的RAII边界中,并在现代C ++中使用这些强大的安全包装类码。)

You can use std::vector<Point> , calling Foo( &v[0], v.size() ) . 可以使用std::vector<Point> ,调用Foo( &v[0], v.size() ) But managing the memory here could be tricky, since Point apparently doesn't provide any clean copy and assignment; 但是在这里管理内存可能很棘手,因为Point显然没有提供任何干净的副本和分配; a custom deleter in the allocator will be called for each element, even if it is copied. 即使复制了每个元素,也会为每个元素调用分配器中的自定义删除器。

If the vector should actually own the points, then you can wrap it in a more complex class, which calls AllocPoint for each insertion (and inserts the results), and DeallocPoint for each removal (and for everything remaining in the vector on destruction). 如果载体应竟是自己的点,那么你可以在一个更复杂的类,它调用它包AllocPoint每个插入(并插入结果),并DeallocPoint每个去除(以及一切留在销毁向量)。 This class should not allow write access to the Point (non-const operator[] , non-const iterators, etc.), however, since this would allow changing any pointers in Point , and loosing what is needed for DeallocPoint to work correctly. 这个类应该允许写访问Point (非const operator[]非const迭代器等),但是,因为这将允许改变任何指针Point ,和失去什么是需要DeallocPoint正常工作。 Presumably, there other functions for manipulating Point ; 据推测,还有其他操作Point功能; you'll have to arrange for these to be available through the wrapper interface. 你必须安排通过包装器界面提供这些。

"You" could write a simple wrapper to free the memory: “你”可以编写一个简单的包装来释放内存:

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())

But this seems a little "adhoc". 但这似乎有点“特别”。 What's a more standard/reusable solution? 什么是更标准/可重复使用的解决方案?

You could use a standard vector with a custom allocator, that invoke AllocPoint on construct method and DeallocPoint() on destruct method. 您可以使用带有自定义分配器的标准向量,它在构造方法上调用AllocPoint,在析构方法上调用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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM