如何利用函数指针在c语言中实现面向对象编程

面向过程编程

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语言级别实现面向对象思想的常见技术,所以我们要非常熟练的了解和掌握这种技巧。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值