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

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

  1. class RCObj {
  2. // implement the ref counting mechanism
  3. int add_ref();
  4. int del_ref();
  5. int ref_count();
  6. public:
  7. virtual ~RCObj() = 0;
  8. int ref() const {
  9. return add_ref();
  10. }
  11. int unref() const {
  12. if (ref_count() == 0 || del_ref() == 0 ) {
  13. delete this;
  14. return 0;
  15. }
  16. return ref_count();
  17. }
  18. };
  19. class A : RCObj {
  20. public:
  21. A();
  22. int foo();
  23. };
  24. class B {
  25. A *_a;
  26. public:
  27. B(A *a) : _a(a) {
  28. a->ref();
  29. }
  30. ~B() {
  31. a->unref();
  32. }
  33. };
  34. int main() {
  35. A *a = new A(); // (count: 0)
  36. a->ref(); // 'a' ref here (count: 1)
  37. B *b1 = new B(a); // 'a' ref here (count: 2)
  38. if (1 + 1 == 2) {
  39. B *b2 = new B(a); // 'a' ref here (count: 3)
  40. delete b2; // 'a' unref, but not deleted (count: 2)
  41. }
  42. delete b1; // 'a' unref, but not deleted (count: 1)
  43. a->unref(); // 'a' unref and deleted (count: 0)
  44. }

上面的例子中,类A的实例a是一个引用计数对象,不能随便删除,因为它在对象b1b2间引用。A从引用计数对象(Reference Counted ObjectRCObj继承,RCObj实现了ref()和unref()惯用方法。

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

  1. %module example
  2. ...
  3. %feature("ref") RCObj "$this->ref();"
  4. %feature("unref") RCObj "$this->unref();"
  5. %include "rcobj.h"
  6. %include "A.h"
  7. ...

当新对象传递到Python时,在refunref特征处指定的代码就会执行,当Python试图释放代理对象的实例时也一样。

在Python这一边,对引用计数对象的使用个与对其他对象的使用没什么两样:

  1. def create_A():
  2. a = A() # SWIG ref 'a' - new object is passed to python (count: 1)
  3. b1 = B(a) # C++ ref 'a (count: 2)
  4. if 1 + 1 == 2:
  5. b2 = B(a) # C++ ref 'a' (count: 3)
  6. return a # 'b1' and 'b2' are released and deleted, C++ unref 'a' twice (count: 1)
  7. a = create_A() # (count: 1)
  8. exit # 'a' is released, SWIG unref 'a' called in the destructor wrapper (count: 0)

注意,用户不必要显式地调用a->ref()a->unref()(也没必要调用delete a)。取而代之的是,SWIG在需要的时候自动处理对它们的调用。如果用户不给类型指定ref/unref特征,SWIG会生成类似下面的代码:

  1. %feature("ref") ""
  2. %feature("unref") "delete $this;"

换句话说就是,当心对象传递到Python时,SWIG不会做任何特殊的事情,当Python释放代理实例是总会delete底层对象。

%newobject特征被设计用来指导目标语言获取返回对象的所有权。当与带ref特征的类型一起使用时,额外会增加对ref特性的支持。考虑包装如下的工厂函数:

  1. %newobject AFactory;
  2. A *AFactory() {
  3. return new A();
  4. }

AFactory函数现在表现的像调用A的构造函数一样来处理内存:

  1. a = AFactory() # SWIG ref 'a' due to %newobject (count: 1)
  2. exit # 'a' is released, SWIG unref 'a' called in the destructor wrapper (count: 0)