面向过程编程
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;
// 首先,实例化Light
light = (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/jni_internal.cc
方法: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语言级别实现面向对象思想的常见技术,所以我们要非常熟练的了解和掌握这种技巧。