面试的时候发现这个地方掌握的不够好,所以单独复习下,顺便写一篇博客。
这篇文章的所有代码都是用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;
}
(完)
转载于:https://blog.51cto.com/zhweizhi/1853390