面向过程编程
c语言,是一种函数流语言,看他的代码,大部分是call1(data), call2(data)
类似的代码。所以他的主体思想是函数
,把数据
丢进去给函数
执行。
面向对象编程
java语言,和c++一脉相承,继承了面向对象的编程思想。 即,
class A {methodA();methodB();}
这种语言表达的主体,是一个对象。不论有什么要执行,首先诞生一个对象A a = new A();
,然后调用该对象的方法:a.methodA()
这是两种不同的观点,导致语法,使用方式,还有执行过程的差异。
弥合二者的差异
那么,有没有一种办法,可以弥合这两种观点的差异,实现用c语言表达对象,用c语言的形式像java那样调用方法呢?
有的
结构体
c语言有一种语法:
void turnOff() {printf("light off");}void turnOn() {print("light on");}type define struct Switch Switch;struct Light {int num;void *turnOff();void *turnOn();};
可以这样理解上述代码:
struct Light是一个结构体,结构体可以包含基本变量,指针,但是不能定义函数。函数不能定义在结构体中,但是可以定义一个函数指针。函数指针就可以指向具体的函数。
例如想让灯打开,关闭,我们这样写:
Light *light;// 首先,实例化Lightlight = (Light *) malloc(sizeof(Light));// 接着,让函数指针指向一个函数light->turnOff = turnOff;light->turnOn = turnOn;// 最后,模拟对象调用方法的形式调用函数light->turnOn();light->turnOff();
struct 里函数指针的使用目的
上面讲到函数指针,他的用法已经明白,其实目的也很清楚了,就是实现面向对象的思想,让函数和对象关联,可以从对象(结构体)直接调用关联函数。
函数表(Function Table)的意义,和结构的关系
对于结构体中的函数指针,通常不止一个,可能7,8个,上十个都挺常见的,我们为了reuse
, 产生了“函数表”的概念。
struct LightTable {void *turnOn();void *turnOff();}struct Light {int num;struct LightTable table;}
这段代码就是讲,把函数指针独立出来,形成一个结构体,可以单独产生对象,被复用,节省运行内存了。
说了半天,函数表有啥用呢?
jni调用中使用函数表的一个实例:
Android JNIEnv 用到的Function Table
为什么要讲上面的函数指针呢?因为他非常重要,在android源码中被大量用到:
先来看看android 5.0源码中的jni_internal.h
文件中有这样一个函数表定义:
static JNINativeMethod gMethods[] = {NATIVE_METHOD(Runtime, freeMemory, "!()J"),NATIVE_METHOD(Runtime, gc, "()V"),NATIVE_METHOD(Runtime, maxMemory, "!()J"),NATIVE_METHOD(Runtime, nativeExit, "(I)V"),NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;"),NATIVE_METHOD(Runtime, totalMemory, "!()J"),};
而在源码文件runtime/
方法:RegisterNativeMethods
是这样实现(删除若干不必要代码):
static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods, jint method_count, bool return_errors) {mirror::Class* c = soa.Decode<mirror::Class*>(java_class);for (jint i = 0; i < method_count; ++i) {const void* fnPtr = methods[i].fnPtr;mirror::ArtMethod* m = c->FindDirectMethod(name, sig);if (m == nullptr) {m = c->FindVirtualMethod(name, sig);}m->RegisterNative(soa.Self(), fnPtr, is_fast);}return JNI_OK;}
我们看m->RegisterNative(soa.Self(), fnPtr, is_fast)
这一行,即是把函数指针fnPtr
作为参数注册到对象(结构体)m
中。
总结
函数指针当做结构体或者对象
的属性,在android源码中非常常见,他是一种c语言级别实现面向对象思想的常见技术,所以我们要非常熟练的了解和掌握这种技巧。