Python C Api 和正确处理 C++ 类中的内存

Posted

技术标签:

【中文标题】Python C Api 和正确处理 C++ 类中的内存【英文标题】:Python C Api and correct handling of memory in C++ classes 【发布时间】:2013-01-07 13:34:40 【问题描述】:

我想知道如何处理以下问题。在我的 C++ 类中,我有一个辅助 PyObject 指针。

class Foo

     public:
     // Should I new the dictionary here in constructor?
     Foo()
     

     
     // Must decrease the reference count or explicitly delete the dictionary?
     ~Foo()
     
         Py_DECREF(myDictionary);
     

     void sync()
     
          myDictionary = PyDict_New();
          for (int i=0; i<myInternalData.size(); i++)
          
                  PyObject *key =  PyInt_FromLong(i);
                  PyObject *val = PyInt_FromLong(myInternalData.at(i));
                  PyDict_SetItem(dict,key,val);
                  Py_DecRef(key);
                  Py_DecRef(val);
          
     

     private:
     PyObject *myDictionary;
     std::vector<int> myInternalData;

在我的 C++ 代码中,myInternalData 结构偶尔会更新或调整大小,我想知道如何处理我的 python 字典的正确内存分配。

我不知道如何释放与其关联的内存,或者如何正确地使其与我的内部 std::vector 保持同步,而不会破坏堆或引发内存泄漏。

对 Python C API 有帮助吗?我应该用PyObject_Del 释放PyDict,然后再次重新分配它吗?有人建议另一种方法?

【问题讨论】:

你经常打电话给sync吗?据我所知,这可能是内存泄漏的来源,因为您正在分配新对象。另外(我在这里可能错了),我认为解除分配字典的适当方法可能是将 ref 计数器减少到零(docs.python.org/2/c-api/refcounting.html)。您是否尝试使用 valgrind 来查看您在哪里得到了内存泄漏? 【参考方案1】:

我不清楚你为什么在 Python 中使用字典, 当您使用一组连续的整数进行索引时,从 0 开始。 但是:在使用它之前,你必须做一个PyDict_New 来 创建字典。之后,当您重新同步时,您 应该在开始之前清除字典,使用 PyDict_Clear,而不是重新分配一个新的..没有别的 应该是必要的。 (如果你重新分配一个新的,就像你在 你的代码,你应该减少旧的引用计数 一个第一。但是 Python 端的任何代码都引用了 旧的将继续引用旧的; PyDict_Clear 是 可能是更好的解决方案。)

另外,你应该注意临时 Python 对象的位置 参与其中。目前,没有其他必要了,因为 您只在循环中使用 Python(以及 C)函数,并且 它们不能触发 C++ 异常。永远改变代码 稍微,这可能不再是这种情况。作为基本规则, 我发现您应该将PyObject* 包装在一个类中 析构函数调用Py_DecRef,而不是显式调用它, 可能会因为异常而错过电话。

【讨论】:

我在构造函数中移动了PyDict_New,现在在sync方法中我只调用PyDict_Clear(myDictionary)。我还在析构函数中添加了Py_DECREF(myDictionary),现在所有的内存泄漏都被消除了!

以上是关于Python C Api 和正确处理 C++ 类中的内存的主要内容,如果未能解决你的问题,请参考以下文章

在c语言中怎样用c++类中的对象调用c++类中的方法?

使用 python-c-api 调用的 Cython 回调段错误

深入C++中API的问题详解_C 语言

在 C++ 类中访问 C 风格的回调

Python C API - 重新加载模块

Python C API 和 C++ 函数