6.24 智能指针与operator->()

在一些C++程序中,通常使用智能指针和代理类来包装对象。有时候它们被用来实现自动内存管理(引用计数)或持久化。典型情况下智能指针用模板类定义,重载了操作符operator->。其他类被该类包装。比如:

  1. // Smart-pointer class
  2. template<class T> class SmartPtr {
  3. T *pointee;
  4. public:
  5. SmartPtr(T *p) : pointee(p) { ... }
  6. T *operator->() {
  7. return pointee;
  8. ...
  9. }
  10. };
  11. // Ordinary class
  12. class Foo_Impl {
  13. public:
  14. int x;
  15. virtual void bar();
  16. ...
  17. };
  18. // Smart-pointer wrapper
  19. typedef SmartPtr<Foo_Impl> Foo;
  20. // Create smart pointer Foo
  21. Foo make_Foo() {
  22. return SmartPtr<Foo_Impl>(new Foo_Impl());
  23. }
  24. // Do something with smart pointer Foo
  25. void do_something(Foo f) {
  26. printf("x = %d\n", f->x);
  27. f->bar();
  28. }
  29. // Call the wrapped smart pointer proxy class in the target language 'Foo'
  30. %template(Foo) SmartPtr<Foo_Impl>;

这种方法的关键点是定义operator->()方法,被智能指针包装的对象属性的访问时透明的。例如,这样的表达式:

  1. f->x
  2. f->bar()

被透明的映射到:

  1. (f.operator->())->x;
  2. (f.operator->())->bar();

当生成包装时,SWIG尽量模拟这种扩展功能。当在类中无论何时遇到operator->(),SWIG查看它的返回值,通过它来访问底层对象的属性。例如,对上面代码的包装可能看起来像这样:

  1. int Foo_x_get(Foo *f) {
  2. return (*f)->x;
  3. }
  4. void Foo_x_set(Foo *f, int value) {
  5. (*f)->x = value;
  6. }
  7. void Foo_bar(Foo *f) {
  8. (*f)->bar();
  9. }

这些包装代码使用智能指针的实例作为参数,但是通过解引用获取对operator->()操作符返回的对象访问。你应该仔细对比这里的包装代码和本章前面的包装代码(它们稍许不同)。

结果是访问方式与在C++中相似。例如,你可以在Python中这样做:

  1. >>> f = make_Foo()
  2. >>> print f.x
  3. 0
  4. >>> f.bar()
  5. >>>

当通过智能指针生成包装代码时,SWIG尽力为所有通过operator->()访问的方法和属性生成包装代码。这包括通过继承能够被访问的任何成员。但是,有一些限制:

  • 成员变量和方法通过智能指针被包装。枚举、构造器和析构器不被包装。

  • 如果智能指针类和底层的对象都定义了相同名字的方法了变量的话,则智能指针版优先。例如,如果有如下代码:

    1. class Foo {
    2. public:
    3. int x;
    4. };
    5. class Bar {
    6. public:
    7. int x;
    8. Foo *operator->();
    9. };

    则,对Bar->x包装后访问的就是Bar中的x,而不是Foo中的x

如果你的目的是仅仅在接口文件中暴露智能指针,就没必要包装智能指针类和底层对象的类了。但是,如果想让本节介绍的技术得以工作,你必须要告诉SWIG这两个类。只包装智能指针类的话,可以使用%ignore指令。例如:

  1. %ignore Foo;
  2. class Foo { // Ignored
  3. };
  4. class Bar {
  5. public:
  6. Foo *operator->();
  7. ...
  8. };

还有一种方法是,你可以通过%import指令,导入独立的文件来导入Foo的定义。

注意:当类定义了operator->()后,操作符本身被包装成__deref__()方法。例如:

  1. f = Foo() # Smart-pointer
  2. p = f.__deref__() # Raw pointer from operator->

注意:使用%ignore指令可以忽略operator->()。例如:

  1. %ignore Bar::operator->;

注意:SWIG-1.3.14加入了对智能指针的支持。