如何删除SWIG生成的Python包装中的堆对象?

6 投票
1 回答
2551 浏览
提问于 2025-04-17 17:45

我正在开发一个Python模块,同时也在使用一个C++库。在C++代码中,我有一个函数,它返回一个堆对象,代码如下。

MyClass* func()
{
  MyClass* myclass = new MyClass();
  return myclass;
}

但是当我在Python中使用这个函数时,我无法删除返回的对象。

myclass = func()
del myclass # still remains in memory

有没有人能告诉我,如何在Python代码中删除这个返回的对象呢?

我可以把返回值的类型从MyClass*改成MyClass,这样就可以避免内存泄漏。但我不想改动C++代码,因为这个C++库已经在其他地方使用了。

1 个回答

8

请查看SWIG文档第11.2节中的%newobject%typemap(newfree)指令。

以下是文档中的引用:

在某些应用程序中,一个常见的问题是如何正确管理对象的所有权。比如,考虑这样一个函数:

Foo *blah() {
   Foo *f = new Foo();
   return f;
}

如果你把函数blah()包装起来,SWIG并不知道返回值是一个新分配的对象。因此,生成的扩展模块可能会导致内存泄漏(SWIG比较保守,除非它确定返回的对象是新创建的,否则不会删除对象)。

为了解决这个问题,你可以使用%newobject指令给代码生成器提供额外的提示。例如:

%newobject blah;
Foo *blah();

%newobject的工作方式和%rename以及%exception完全一样。换句话说,你可以像之前一样把它附加到类成员和带参数的声明上。例如:

%newobject ::blah();                   // Only applies to global blah
%newobject Object::blah(int,double);   // Only blah(int,double) in Object
%newobject *::copy;                    // Copy method in all classes
...

当提供了%newobject时,许多语言模块会安排接管返回值的所有权。这意味着当返回值不再使用时,可以自动进行垃圾回收。不过,这完全取决于目标语言(某些语言模块可能会选择忽略%newobject指令)。

与%newobject紧密相关的是一个特殊的类型映射。这个“newfree”类型映射可以用来释放新分配的返回值。它只在应用了%newobject的方法中可用,通常用于清理字符串结果。例如:

%typemap(newfree) char * "free($1);";
...
%newobject strdup;
...
char *strdup(const char *s);

在这种情况下,函数的结果是目标语言中的一个字符串。由于这个字符串是原始结果的副本,strdup()返回的数据不再需要。示例中的“newfree”类型映射就是用来释放这块内存的。

撰写回答