6.25 C++引用计数对象——ref/unref特征
另外一个C++中相似的概念是引用计数对象。考虑如下的代码:
class RCObj {// implement the ref counting mechanismint add_ref();int del_ref();int ref_count();public:virtual ~RCObj() = 0;int ref() const {return add_ref();}int unref() const {if (ref_count() == 0 || del_ref() == 0 ) {delete this;return 0;}return ref_count();}};class A : RCObj {public:A();int foo();};class B {A *_a;public:B(A *a) : _a(a) {a->ref();}~B() {a->unref();}};int main() {A *a = new A(); // (count: 0)a->ref(); // 'a' ref here (count: 1)B *b1 = new B(a); // 'a' ref here (count: 2)if (1 + 1 == 2) {B *b2 = new B(a); // 'a' ref here (count: 3)delete b2; // 'a' unref, but not deleted (count: 2)}delete b1; // 'a' unref, but not deleted (count: 1)a->unref(); // 'a' unref and deleted (count: 0)}
上面的例子中,类A的实例a是一个引用计数对象,不能随便删除,因为它在对象b1和b2间引用。A从引用计数对象(Reference Counted Object)RCObj继承,RCObj实现了ref()和unref()惯用方法。
为了告诉SWIG,RCObj和所有从它继承的类是引用计数对象,使用ref和ubref特征。还可以使用1%refobject和%unrefobject。例如:
%module example...%feature("ref") RCObj "$this->ref();"%feature("unref") RCObj "$this->unref();"%include "rcobj.h"%include "A.h"...
当新对象传递到Python时,在ref和unref特征处指定的代码就会执行,当Python试图释放代理对象的实例时也一样。
在Python这一边,对引用计数对象的使用个与对其他对象的使用没什么两样:
def create_A():a = A() # SWIG ref 'a' - new object is passed to python (count: 1)b1 = B(a) # C++ ref 'a (count: 2)if 1 + 1 == 2:b2 = B(a) # C++ ref 'a' (count: 3)return a # 'b1' and 'b2' are released and deleted, C++ unref 'a' twice (count: 1)a = create_A() # (count: 1)exit # 'a' is released, SWIG unref 'a' called in the destructor wrapper (count: 0)
注意,用户不必要显式地调用a->ref()或a->unref()(也没必要调用delete a)。取而代之的是,SWIG在需要的时候自动处理对它们的调用。如果用户不给类型指定ref/unref特征,SWIG会生成类似下面的代码:
%feature("ref") ""%feature("unref") "delete $this;"
换句话说就是,当心对象传递到Python时,SWIG不会做任何特殊的事情,当Python释放代理实例是总会delete底层对象。
%newobject特征被设计用来指导目标语言获取返回对象的所有权。当与带ref特征的类型一起使用时,额外会增加对ref特性的支持。考虑包装如下的工厂函数:
%newobject AFactory;A *AFactory() {return new A();}
AFactory函数现在表现的像调用A的构造函数一样来处理内存:
a = AFactory() # SWIG ref 'a' due to %newobject (count: 1)exit # 'a' is released, SWIG unref 'a' called in the destructor wrapper (count: 0)
