面试的时候发现这个地方掌握的不够好,所以单独复习下,顺便写一篇博客。
这篇文章的所有代码都是用C实现的,这是由于C没有面向对象的特性,所以我希望用函数指针把C的结构体弄得有一点面向对象的感觉。
先介绍函数指针、回调函数,然后用C结合回调函数实现一个有面向对象风格的链表
函数指针:
函数指针,就是指向函数的指针
基本的函数指针(无参数无返回值)
//函数指针 void fun( ) { printf("fun()\n"); } int main() { //void *fp1() = &fun void (*fp1)(); fp1 = fun; fp1(); return 0; }
有参数
//带参数 void fun(int val1, int val2) { printf("val1 = %d, val2 = %d\n", val1, val2); } int main() { void(*fp)( int val1, int val2 ) = &fun; fp(1, 2); return 0; }
有参数及返回值
//带参数及返回值 int fun(int val1, int val2) { return val1 + val2; } int main() { int(*fp)(int val1, int val2) = &fun; int ret = fp(1, 2); printf("%d\n", ret); return 0; }
但是,这样定义函数指针非常影响可读性,特别是对于复杂的函数,比如参数或者返回值也是函数指针类型的情况,因此,通常可以用typedef 如下:
//typedef typedef int (*FP)(int val1, int val2); int fun(int val1, int val2) { return val1 + val2; } int main() { FP fp1 = &fun; int ret = fp1(1, 2); printf("%d\n", ret); return 0; }
回调函数:
而回调函数的实现,则利用了函数指针
简单的说,回调函数是一个函数,它的参数是函数指针和该指针所指向函数的参数,作为原本执行函数和目标函数之间的中介
为什么要用回调函数呢?回调函数的优势在于它的灵活性
通常我们调用函数,是被编译器最终转化成汇编语言、二进制文件的,板上钉钉的事情,这是事先决定好的,是静态的
但回调函数就不一样了,回调函数接受的参数,只是一个未知的函数的地址,是只有在执行时才可知的,是动态的
验证的标准就是,对于回调函数,如果传入的参数不是函数的地址,而是其他某个变量的地址,编译仍能正确执行,只有在运行时才出错。
上述的“动态特性” 是不是很像 C++中发生继承时的动态绑定呢?
简单的应用:
这里不得不顺便说一下,C++的相同自定义类的不通对象中的成员方法是公用的,怎么测试呢,看下面的C++代码
class Test { public: int _val = 20; void _Print() { std::cout << "hello?"" << endl; } }; int main() { Test *pt = NULL; t1._Print();//能执行 t1._val = 0; //崩溃,因为t1并没有指向一个Test类型的实例,因此当然访问不到_val成员了 }
执行上述的代码,会发现即使整个代码并没有 Test对象的实例化,但仍能正确地调用Test中的_Print方法,这时由于C++类中的成员方法是公用的,并且并不是存放在每个类的对象中的,这么做当然是为了节省空间,那么我们能不能稍微借助一下这样的设计思路,用C简单的实现一个C++的类呢?
下面我将给结构体struct中定义函数指针,看看效果:
void fun() { printf("hello\n"); } typedef struct ST { void(*_pf)(); }ST; void Set( ST **st ) //类似构造函数 { *st = (ST*)malloc(sizeof(ST)); (*st)->_pf = &fun; } int main() { ST *s1 = NULL; Set(&s1); //初始化,这里貌似没办法在内部访问了 s1->_pf(); //像不像C++呢 return 0; }
在这段代码中,类ST是一个函数指针,而main函数的前两行则可以认为是C++中的构造函数,它的作用是将结构指针进行赋值,我定义这个函数是由于C中的结构体中的变量不支持在结构体内初始化。
然后我就可以像C++调用成员函数的方法了。
当然,这并不是原汁原味的C++的实现方式,C++是通过静态绑定的方式,在编译时就确定函数的位置的,而我是用动态的方式实现的,我的结构体中需要声明若干函数指针。
OK,接下来再实现一个简单的链表
struct LinkListNode; typedef void(*pPushBack)(struct LinkListNode *This, int data); struct LinkListNode* BuyNode(data); typedef struct LinkListNode { int _val; struct LinkListNode *_next; pPushBack _PushBack; }LinkListNode; void PushBack(LinkListNode *This, int data) { if (This == NULL) { This = BuyNode(data); } LinkListNode *NewNode = BuyNode(data); LinkListNode *cur = This; while (cur && cur->_next != NULL) { cur = cur->_next; } cur->_next = NewNode; } LinkListNode* BuyNode(int data) { LinkListNode *NewNode = (LinkListNode*)malloc(sizeof(LinkListNode)); NewNode->_val = data; NewNode->_next = NULL; NewNode->_PushBack = &PushBack; return NewNode; } void Init(struct LinkListNode **This, int data) { (*This) = BuyNode(data); (*This)->_val = data; (*This)->_next = NULL; (*This)->_PushBack = &PushBack; } int main() { LinkListNode *Node1 = NULL; Init(&Node1, 1); Node1->_PushBack(Node1, 2); Node1->_PushBack(Node1, 3); Node1->_PushBack(Node1, 4); return 0; }
(完)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。