在STL容器中存储指向堆对象的指针,以便以后重新分配

时间:2012-10-31 15:53:22

标签: c++ stl c++11 cuda

如何在STL容器中存储任意数量的动态创建的实例(不同类型),以便以后只有容器才能释放内存?

它应该像这样工作:

std::vector< void * > vec;
vec.push_back( new int(10) );
vec.push_back( new float(1.) );

现在,如果vec超出范围,则会破坏指向实例的指针,但不会释放intfloat的内存。显然我不能这样做:

for( auto i : vec )
  delete *i;

因为void*不是指向对象的指针类型。

您可以反对并认为这不是一个好主意,因为无法访问向量的元素。这是对的,我自己也不会访问它们。 NVIDIA驱动程序将访问它们,因为它只需要地址(void*就可以了)它的内核调用参数。

我想这里的问题是它可以是存储的不同类型。想知道union是否可以做到这一点,以防有人想将此作为参数传递给cuda内核。

内核接受不同类型的参数,并通过遍历表达树(表达式模板)来收集,而表达式树(表达式模板)预先不知道类型。因此,在访问叶子时,您将存储参数。它只能是void *,内置类型为int,float等。

可以在内核启动后立即删除向量(启动是异步的,但驱动程序先复制参数然后继续主机线程)。第二个问题:每个参数都向驱动程序传递一个void *。无论是int,float还是void *。所以我猜一个人可以分配比所需更多的内存。我认为联合物可能值得一看。

5 个答案:

答案 0 :(得分:5)

您可以使用您想要支持的每种类型的一个向量。

虽然这对void*矢量的概念有很大的改进,但它仍然很臭。

这听起来像是一个 XY问题:你有一个问题X,你想象一个解决方案Y,但是如果没有某种巧妙的适应性,Y显然是行不通的,所以问一下Y.相反,应该询问真正的问题X.哪个是?

答案 1 :(得分:1)

好的,FWIW

我建议使用与malloc结合的就地新内容。这样做可以让你在向量中存储创建为void*的指针。然后当向量完成时,它可以简单地迭代并调用free()

void* ptr = malloc(sizeof(int));
int* myNiceInt = new (ptr) int(myNiceValue);
vec.push_back(ptr);

//at some point later iterate over vec
free( *iter );

我相信在这种情况下,这将是解决问题的最简单方法,但请接受这是一个“C”类似的答案。

只是说';)

答案 2 :(得分:0)

“NVIDIA驱动程序”听起来像是一个C界面,所以malloc并不是一个疯狂的建议。

另一种选择,正如你所建议的那样,是使用联合......但是你还需要在一个平行向量中存储“标签”来记录元素的实际类型,这样你就可以转换成适当的类型删除。

简而言之,您必须先将void *转换为适当的类型,然后才能delete它。 “C ++方式”将是一个带有虚拟析构函数的基类;当它指向任何子类的实例时,你可以调用delete。但是,如果您使用的库已经确定了类型,那么这不是一个选项。

答案 3 :(得分:0)

如果您可以控制类型,则可以为它们创建抽象基类。给该类一个虚拟析构函数。然后你可以拥有std::vector<Object*>并迭代它以删除任何继承自Object的内容。

你可能需要第二个std::vector<void*>指向实际值,因为Object*可能首先击中vtable。像virtual void* ptr() { return &value; }这样的第二个虚函数在这里很有用。如果它需要对象的大小,你也可以添加它。

你可以使用这样的模板模式:

template<typename T>
class ObjVal : public Object {
    public:
    T val;
    virtual void* ptr() { return &this->val; }
    virtual size_t size() { return sizeof(this->val); }
};

然后你只需输入一次。

这并不是特别节省内存,因为每个Object都会为vtable选择至少一个额外的指针。

但是,new int(3)的内存效率不高,因为你的分配器可能使用超过4个字节。添加该vtable指针可能基本上是免费的。

答案 4 :(得分:-1)

使用多个向量。保持vector<void*>与API交谈(我猜测它需要一个连续的非均匀类型的void *?),但也有一个vector<std::unique_ptr<int>>vector<std::unique_ptr<float>>拥有数据。当您创建new int时,将拥有内存的unique_ptr推送到vector int的{​​{1}},然后将其粘贴到与API兼容的vector上}作为void*。将三个vector捆绑成一个struct,以便在可能的情况下将它们的生命期联系在一起(并且可能是)。

您也可以使用存储变量所有权的单个向量来执行此操作。 vector自制RAII伪 - unique_ptrshared_ptr与自定义驱逐舰,或vector std::function<void()>您的“捆绑” struct的驱逐舰调用,或者你有什么。但我不建议这些选择。