C语言实现封装、继承、多态

    技术2023-06-20  69

    C语言实现封装、继承、多态

    文章目录

    C语言实现封装、继承、多态一. 封装二.继承三. 多态

    一. 封装

    C语言中虽然没有类,但有struct和指针。我们可以在一个struct中存入数据和函数指针,以此来模拟类行为。

    typedef struct _Parent { int a; int b; void (*print)(struct _Parent *This); }Parent; 封装性的意义在于,函数和数据是绑在一起的,数据和数据是绑在一起的。这样,我们就可以通过简单的一个结构指针访问到所有的数据,遍历所有的函数。封装性,这是类拥有的属性,当然也是数据结构体拥有的属性。

    二.继承

    如果要完全地用C语言实现继承,可能有点难度。但如果只是简单的做一下,保证子类中含有父类中的所有成员。这还是不难的。 typedef struct _Child { Parent parent; int c; }Child; 在设计C语言继承性的时候,我们需要做的就是把基础数据放在继承的结构的首位置即可。这样,不管是数据的访问、数据的强转、数据的访问都不会有什么问题。

    三. 多态

    这个特性恐怕是面向对象思想里面最有用的了。要用C语言实现这个特性需要一点点技巧,但也不是不可能的。 #include <stdio.h> #include <stdlib.h> //模拟一个类A typedef struct A { void *vptr;//虚函数指针 //下面内容可以删掉,写上的目的:看着像一个类 //成员变量 int a; int b; void initA(A *p, int a, int b); //虚函数 void dong1(); void dong2(); }A; //实现虚函数 void dong1() { printf("基类 dong1\n"); } void dong2() { printf("基类 dong2\n"); } //模拟虚表结构 typedef struct { void(*v1)(); void(*v2)(); }Vtable; //模拟一个类中的虚表 Vtable A_Vtable = { &dong1, &dong2 };//精华在这:★a类可以调用dong1 dong2 这两个函数★ //模拟A类的构造函数 void initA(A *p, int a, int b)//第一个是this指针,在c++中也会默认传一个this指针 { p->vptr = &A_Vtable; p->a = a; p->b = b; } /// //模拟派生类 B typedef struct B { A a;//父类的内容 //下面内容可以删掉,写上的目的:看着像一个类 int b; void dong11(); void dong66(); void initB(B* p, int a, int b); }B; //模拟派生类的虚函数 void dong11()//模拟覆盖dong1这个函数 { printf("派生类 dong11\n"); } void dong66() { printf("派生类 dong66\n"); } //模拟子类虚表,增加了一个函数dong66 typedef struct { Vtable vtable; void(*p)(); }Vtable2; //子类的虚表 //注意这里大括号一个不能少 //★b类可以调用dong11、dong2、dong66 这三个函数★ Vtable2 B_vtable = { { &dong11, &dong2 }, &dong66 }; //B类构造函数 //为什么要a参数呢?初始化基类 void initB(B* p,int a, int b) { //************重点************** //创建B类,但是需要初始化A类。继承A类中的虚表指针 //initA((A*)p, a, b); //我们这里用B类自己的虚表,一个类一个虚表 p->a.vptr = &B_vtable; p->b = b; } /// //以下内容全是模拟 //测试一下A类 void test1() { A aa; initA(&aa, 10, 20); ((Vtable*)aa.vptr)->v1(); ((Vtable*)aa.vptr)->v2(); } //测试一下B类 void test2() { //创建B类 B *b = (B*)malloc(sizeof(B)); initB(b, 10, 20); //转成A类 A *a = (A*)b; //现在表面上是父类,而我们可以调用父类没有而子类有的方法 ((Vtable2*)(a->vptr))->p(); printf("\n------见证奇迹的时候到了,实现多态------\n\n"); ((Vtable*)(a->vptr))->v1();//子类覆盖了父类对象,调用的就是子类对象 ((Vtable*)(a->vptr))->v2();//子类没有覆盖父类对象,调用的就是父类对象 } //test3用来解惑的。其实我们b类不靠A就能调用父类,子类方法,test2实现的多态就是为了模拟C++ void test3() { B *b = (B*)malloc(sizeof(B)); initB(b, 10, 20); //下面两个地址是一样的,这里得仔细考虑。我也是调试了好久才发现其中的奥秘 printf("%d\n", b->a.vptr); printf("%d\n", ((A*)b)->vptr); ((Vtable*)(b->a.vptr))->v1(); ((Vtable*)(b->a.vptr))->v2(); ((Vtable2*)(b->a.vptr))->p(); } int main() { test1(); printf("---------------------------------------------\n\n"); test2(); printf("---------------------------------------------\n\n"); test3(); return 0; } /* **************************上面看着费劲----光看下面就行了 typedef struct { void(*v1)(); void(*v2)(); }Vtable; Vtable A_Vtable = { &dong1, &dong2 };//如果不单独测试A 这句话就是废话 typedef struct { //Vtable2有一个Vtable,所以Vtable2可以转化为Vtable使用v1 v2。 Vtable vtable; //但是我们赋值的时候是Vtable2 所以真真调用的时候,还是从Vtable2中调用 void(*p)(); }Vtable2; Vtable2 B_vtable = { { &dong11, &dong2 }, &dong66 }; 所以,至始至终我们利用的就是Vtable2指向的函数。可以调用v1那是因为Vtable2中有Vtable,而Vtable中有v1 */
    Processed: 0.009, SQL: 9