6.25 C++引用计数对象——ref/unref特征

另外一个C++中相似的概念是引用计数对象。考虑如下的代码:

class RCObj {
  // implement the ref counting mechanism
  int 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是一个引用计数对象,不能随便删除,因为它在对象b1b2间引用。A从引用计数对象(Reference Counted ObjectRCObj继承,RCObj实现了ref()和unref()惯用方法。

为了告诉SWIG,RCObj和所有从它继承的类是引用计数对象,使用refubref特征。还可以使用1%refobject%unrefobject。例如:

%module example
...
%feature("ref") RCObj "$this->ref();"
%feature("unref") RCObj "$this->unref();"
%include "rcobj.h"
%include "A.h"
...

当新对象传递到Python时,在refunref特征处指定的代码就会执行,当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)

results matching ""

    No results matching ""