有一个完整的OpenCV c++函数的C包装器列表,如下所示。它们都返回一个"new"。我不能改变它们,因为它们正在成为OpenCV的一部分,它会使我的库完美地有一个持续更新的框架来包裹。
Mat* cv_create_Mat() {
return new Mat();
}
我不能为c++函数重写C包装器,所以我写了一个像下面这样的删除包装器,我试图释放的内存是一个Mat*, Mat是一个OpenCV c++类…下面的删除包装器可以工作。绝对没有内存泄漏。
我有很多其他的C包装器OpenCV c++函数,虽然,返回一个新的指针…至少有10或15个,我的意图是不必为所有这些都编写单独的删除包装器。如果你能告诉我如何写一个删除包装,将释放任何指针后,它不必被告知哪一种类型的释放和快速,太棒了。
这些都是我的意图,我知道你们这些伟大的程序员可以帮助我解决这个问题:)…简而言之……我有cvsvmpparams *, Brisk*, RotatedRect*, CVANN_MLP*指针还有一些其他的,都需要与一个包装器释放…一个去包装c++的delete将释放任何东西…在这方面任何帮助都是非常宝贵的。void delete_ptr(void* ptr) {
delete (Mat*)ptr;
}
编辑:我需要你们两个人中的一个告诉我如何运行你们发布的代码…当我把Emacs g++放在main上面并使用Free(cv_create_Mat)运行时,注册表版本不起作用;一个新的Mat*创建者和存根以相同的方式运行得到5个错误消息。我需要精确的编译指令。我的目的是能够把这个编译成。so文件,你真的很关注这篇文章,虽然,我很感激,谢谢你
这样如何,然后让编译器处理所有的专门化:
template <typename T>
void delete_ptr(T *ptr) {
delete ptr;
}
delete
操作符不仅释放内存,还调用析构函数,并且必须在类型化指针(而不是void*
)上调用,以便它知道要调用哪个类的析构函数。您需要为每种类型使用单独的包装器。
对于没有析构函数的POD类型,您可以分配malloc()
而不是new
,以便调用者可以使用free()
。
我建议不要使用通用的delete_ptr
函数。
由于创建和删除是成对的,我将创建一个用于特定类型的创建和删除。
Mat* cv_create_Mat();
void cv_delete_Mat(Mat*);
如果你这样做,你正在处理的对象类型就会少一些歧义。此外,cv_delete_Mat(Mat*)
的实现将更不容易出错,并且必须承担更少的责任。
void cv_delete_Mat(Mat* m)
{
delete m;
}
这样的泛型操作只能通过删除类型信息(void*
)或单独确保所有包装器函数存在来实现。
C的ABI不允许函数重载,c++的delete
关键字正是你所要求的那种通用包装器。
也就是说,你可以做一些事情,但没有一件比你已经提出的建议更简单。您编写的任何泛型c++代码都将无法从C中调用。
你可以给你的对象添加知道如何销毁自己的成员,例如
类CVersionOfFoo:公共Foo {…静态void deleter(CVersionOfFoo* p){删除p;}
};但是c无法访问。
最后一个选项是设置某种形式的手动注册表,其中对象将其指针与删除函数一起注册。但是,这将是更多的工作,更难调试比仅仅编写包装。
—EDIT—
注册表例子;如果你有c++ 11:
#include <functional>
#include <map>
/* not thread safe */
typedef std::map<void*, std::function<void(void*)>> ObjectMap;
static ObjectMap s_objectMap;
struct Foo {
int i;
};
struct Bar {
char x[30];
};
template<typename T>
T* AllocateAndRegister() {
T* t = new T();
s_objectMap[t] = [](void* ptr) { delete reinterpret_cast<T*>(ptr); };
return t;
}
Foo* AllocateFoo() {
return AllocateAndRegister<Foo>();
}
Bar* AllocateBar() {
return AllocateAndRegister<Bar>();
}
void Free(void* ptr) {
auto it = s_objectMap.find(ptr);
if (it != s_objectMap.end()) {
it->second(ptr); // call the lambda
s_objectMap.erase(it);
}
}
如果你没有c++ 11…您必须创建一个delete函数。
就像我说的,它比你创建的包装器要多。
这并不是c++不能做到这一点的情况——c++完全有能力做到这一点,但是你试图在C中做到这一点而C没有提供自动做到这一点的工具。
核心问题是c++中的delete
需要一个类型,通过C接口传递指针会丢失该类型。问题是如何以一种通用的方式恢复这种类型。以下是一些选择。
请记住delete
做了两件事:调用析构函数和释放内存。
- 每种类型单独的功能。最后一招,你想要避免的。 对于具有普通析构函数的类型,您可以将void指针强制转换为您喜欢的任何类型,因为它所做的只是释放内存。这样就减少了函数的数量。[这是未定义的行为,但它应该工作]
- 使用运行时类型信息恢复指针的type_info,然后将其动态强制转换为正确的类型以删除。
- 修改你的create函数,将指针存储在一个带有type_info的字典中。在删除时,检索类型并使用动态强制转换来删除指针。
尽管如此,我可能会使用选项1,除非有数百种东西。您可以编写带有显式实例化的c++模板来减少所需的代码量,或者编写带有标记粘贴的宏来生成唯一名称。以下是一个示例(编辑过):
#define stub(T) T* cv_create_ ## T() { return new T(); }
void cv_delete_ ## T(void *p) { delete (T*)p; }
stub(Mat);
stub(Brisk);
字典方法的一个优点是用于调试。您可以在运行时跟踪new和delete,并确保它们正确匹配。如果调试非常重要,我会选择这个选项,但它需要更多的代码来完成。