这个月时间过得真快,又到了月底了。回想起来,一个月下来,学到的东西可不少啊,感觉很充实,很好!这次继续总结一下Python调用C++类的点点滴滴吧,开动!
上次说过PyArg_ParseTuple可以很神奇的转化基类与子类的关系,原来这只是在某些条件下才成立的。例如:有这样一个类结构:
class SelectorProtocol
{
//...
};
class CCobject
{
//...
};
class CCNode: public SelectorProtocol, public CCObject
{
//...
};
一开始我打算使用PY_CCObejct*来作为PyArg_ParseTuple的参数,它本来的类型是个PY_CCNode,结果发现
PY_CCObject*->obj(CCObject*)的指针值是错误的,传进去C++函数崩了,原因大概如下:
SelectorProtocol | A |
CCObject | B |
CCNode的内存结构。上函数把Python结构里的CCObject*的起始位置转化到SelectorProtocol处,但是CCNode的内存起始位置是在SelectorProtocol那里,所以使用转化后的CCNode的基类SelectorProtocol的内存指向了前面的不明内存,这就出错了!所以以后处理这种问题可要当心了!也可以总结为:PyArg_ParseTuple只是把指针强转了。
还有一个关于内存释放的死循环问题,原来为了保持脚本特性,在一个PY_Object*作用域外,会判断其C++对象在不在自动释放池里,是则不管了,否则delete掉。如果没有处理回调函数时能很好的处理,但是在PyObject*在CCObject中作为回调使用,例如:
CCNode在释放中,即调用了delete this,在其父类CCObject中有个Py_XDecref(用来减少PyObject*计数),那时如果PyObject*(Python的CCNode对象)计数为一,一调用那个函数,计数变为0,立即调用Python的dealloc,里面又先进行自动释放池查找(原来自动释放池在每个循环都会清空,导致只在调用autorelease那个帧循环在,其他循环不在),结构肯定没找到,调用了delete其C++对象的操作,这刚好形成了一个环!因此现在取消了Python导出类析构函数的delete C++对象操作,这样如果是这样:myNode = CCNode() 没有使用自动释放池,会内存泄露。因此需要再myNode.autoRelease()才保证内存安全释放。或者考虑把PyObject*当做CCObject*的一个成员变量,PyObject*在CCObject*创建时创建,销毁时销毁,这样管理它就比较清晰,也不用担心内存泄露与Py_XDEREF导致的崩溃。现在为了解决那个Py_XDEREF问题,是手动把其计数为一,然后再使用该函数,保证了不内存泄露。
Python调用C++类的细节真多,但自己实现确实学到不少东西,继续加油!GO!