6.13 继承
SWIG支持C++类的继承,允许单个或多重继承,只受目标语言所限。SWIG的类型检查器知道基类与派生类之间的关系,允许指向派生类对象的指针在函数使用。类型检查器可以正确处理指针的强制转换,并且可以安全用于多重继承。
SWIG处理私有和保护继承的方式与C++的精神是相近的,只受目标语言能力的限制。多数情况下,这就意味着,SWIG会解释非公有的继承声明,除针对构造函数与析构函数的隐式策略外,它对代码生成没什么作用。
下面的雷子显式了SWIG如何处理继承。为了清晰,给出了全部的C++代码:
// shapes.i%module shapes%{#include "shapes.h"%}class Shape {public:double x,y;virtual double area() = 0;virtual double perimeter() = 0;void set_location(double x, double y);};class Circle : public Shape {public:Circle(double radius);~Circle();double area();double perimeter();};class Square : public Shape {public:Square(double size);~Square();double area();double perimeter();}
当包装成Python后,我们可以执行线面的操作(用底层的Python访问函数演示):
$ python>>> import shapes>>> circle = shapes.new_Circle(7)>>> square = shapes.new_Square(10)>>> print shapes.Circle_area(circle)153.93804004599999757>>> print shapes.Shape_area(circle)153.93804004599999757>>> print shapes.Shape_area(square)100.00000000000000000>>> shapes.Shape_set_location(square,2,-3)>>> print shapes.Shape_perimeter(square)40.00000000000000000>>>
在这个例子中,创建了Circle和Square的对象。可在对象上调用成员函数:Circle_area ,Square_area等。但是在每个对象上只使用Shape_area完成同样的任务。
关于继承一个非常重要的一点是,底层的访问函数的生成只对那些实际声明的对象有效,在上面的例子中,set_location()函数只能通过Shape_set_location()访问,而不能通过Circle_set_location()或Square_set_location()访问。当然了,Shape_set_location()函数接受从Shape继承的类的任何对象。同样地,属性x和y的访问函数为Shape_x_get(),Shape_x_set(),Shape_y_get()和Shape_y_set()。函数Circle_x_get()不存在——可以使用Shape_x_get()代替。
请注意,底层的访问函数与代理类的方法一一对应,因此C++类方法与生成的代理类也是一一对应的。
注意:为得到最好的结果,SWIG需要所有的基类都要在接口文件中定义。否则,你会得到如下的警告提示:
example.i:18: Warning 401: Nothing known about base class 'Foo'. Ignored.如果任何一个基类没有定义,SWIG还是会生成正确的类型关系。例如,接受
Foo*类型的函数将会接受任何从Foo继承的类的对象,无论SWIG是否包装了Foo类。如果你真的不想为基类生成包装,但是你想关掉警告,可以考虑使用%import指令包含定义了Foo的文件。%import简单地收集类型信息,但不生成包装代码。另外一种方式是,你可以只定义Foo为空类或使用警告抑制。注意:Typedef的名字可以被用作基类。比如:
class Foo {...};typedef Foo FooObj;class Bar : public FooObj { // Ok. Base class is Foo...};同样地,typedef允许为命名的结构体用作基类。比如:
typedef struct {...} Foo;class Bar : public Foo { // Ok....};兼容性注释:从SWIG 1.3.7开始,SWIG只对在类中实际定义的声明生成包装代码。这与SWIG 1.1不同,它继承所有在基类中的声明,生成特定的访问函数,比如:
Circle_x_get(),Square_x_get(),Circle_set_location()和Square_set_location()。这种行为将导致在那些大的类继承体系中生成了大量重复的代码,并且使构建跨多个模块的应用程序变得很麻烦(因为由于访问函数在每一个模块都有重复)。当使用代理类等高级特性时,也不需要有这样的包装器。注意,当开启-fvirtual选项后,将启用进一步的优化,这将避免对定义在基类中的虚拟函数再生成包装代码了。
