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
*/

猜你喜欢

转载自blog.csdn.net/wolfGuiDao/article/details/107104272
今日推荐