C语言实现面向对象

一、面向对象的三个基本特征

1.1 封装

封装就是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别,将抽象得到的数据和行为相结合,形成一个有机的整体,形成“类”,其中数据和函数都是类的成员。

1.2 继承

继承即派生类继承了基类的成员变量和成员函数,使子类拥有和父类相同的行为。

1.3 多态

多态同一个行为具有多个不同表现形式或形态的能力。在C++中具有继承关系的子类对象和父类对象对外提供一系列统一的接口,在外部调用时会根据对象类型产生不同的结果。

二、C语言实现封装

在一个类里面包含了成员变量和成员函数,成员变量代表类的属性,成员函数代表类的行为,C语言本身是一门结构化的语言,不直接直接面向对象的编程,但是面向对象只是一种编程手法,通过使用结构体和函数也可以实现同样的功能。

2.1 成员变量定义和访问控制

以顺序表为例,首先是顺序表数据成员,里面包含了长度信息和一个头指针,在C语言中并不支持模板类型,如果要存储任意类型的数据,只能存储对应数据的首地址,当需要存储的时候把地址放入到表中,拿出来后做强制转换,然后进行数据操作。

Vector是void类型,这样的作用是隐藏内部成员细节,相当于C++中的private成员。

typedef void* VectorNode;
typedef void Vector;

typedef struct
{
    int length;
    VectorNode* head;
}vector_def;

在C++中如果要构建一个对象,这个对象首先会使用构造函数对内部数据进行初始化,并且对象可以构建在堆空间和栈空间上,堆空间的对象可以通过智能指针或者手动释放,栈空间在生命周期结束后就不存在了。

而C语言里面并没有构造函数和析构函数,需要手动构建对象和析构对象,否则就会造成内存泄漏。

在顺序表这个实验里面表的空间是需要动态申请的,因此构造函数也从堆空间申请内存进行对象构建。

2.2 对象的创建和删除

Vector* vector_create(int length)
{
    vector_def* ret = NULL;

    if(length > 0)
    {
        ret = malloc(sizeof(vector_def));
        ret->head = malloc(sizeof(VectorNode) * length);
        if(ret && ret->head)
        {
            ret->length = length;
            for(int i = 0; i < length; i++)
            {
                ret->head[i] = NULL;
            }
        }
    }
    return (Vector*)ret;
}

void vector_clear(Vector* list)
{
    if(list)
    {
        free(((vector_def*)list)->head);
        free(list);
    }
}

在一个类中除了成员变量还有对应的成员函数,C++在调用时候会传递对象地址用于在成员函数中访问成员变量,并且隐藏了实现细节,C语言里面需要显式传递对象地址,这样才能访问到成员变量,并且由于函数参数中包含了对应类型的变量,因此该函数只能被该类使用。

2.3 成员函数的访问控制

下面使用顺序表的基本操作来进行演示,这三个函数是需要提供给外部使用的,相当于公有成员函数:

bool vector_insert(Vector* list, int i, const VectorNode node)
{
    bool ret = true;
    vector_def* obj = (vector_def*)list;

    if(obj && (i >= 0) && (i < obj->length) && node)
    {
        for(int j = obj->length - 1; j > i; j--)
        {
            obj->head[j] = obj->head[j - 1];
        }
        obj->head[i] = node;
    }
    else
    {
        ret = false;
    }
    return ret;
}
VectorNode vector_remove(Vector* list, int i)
{
    VectorNode ret = NULL;
    vector_def* obj = (vector_def*)list;

    if(obj && (i >= 0) && (i < obj->length))
    {
        for(int j = i + 1; j < obj->length; j++)
        {
            obj->head[j - 1] = obj->head[j];
        }
        ret = obj->head[i];
    }
    return ret;
}
bool vector_get(Vector* list, int i, VectorNode* node)
{
    bool ret = true;
    vector_def* obj = (vector_def*)list;

    if(obj && (i >= 0) && (i < obj->length) && node)
    {
        *node = obj->head[i];
    }
    else
    {
        ret = false;
    }
    return ret;
}
int vector_length(Vector* list)
{
    int ret = 0;

    ret = (list) ? ((vector_def*)list)->length : (0);

    return ret;
}

与公有成员函数相对应的还有私有成员函数,不能被外部进行访问,这时可以使用static修饰对应的成员函数,让该函数只能被内部使用。

三、C语言实现继承

3.1 子类继承父类成员变量

子类继承父类之后,在数据成员上面表现为叠加,并且子类可以使用父类的函数。
此处使用链表来进行演示。

typedef void LinkList;
typedef void* LinkListNode;

// 继承父类vector_def
typedef struct
{
    vector_def base;
    struct link_list_node head;
}link_list_def;

// 链表节点定义
struct link_list_node
{
    struct link_list_node* next;
};

创建和销毁链表

LinkList* link_list_create()
{
    link_list_def* list = malloc(sizeof(link_list_def));
    if(list)
    {
        list->base.length = 0;
        list->base.head = NULL;
        list->head.next = NULL;
    }
    return (LinkList*)list;
}

void link_list_clear(LinkList* list)
{
    free(list);
}

3.2 子类使用父类成员函数

使用父类的成员函数获取链表长度:

int main()
{
    LinkList* list = link_list_create();
    
    printf("%d\n", vector_length(list));

    link_list_clear(list);
}
$> ./a.out
0

由于链表的数据插入、移除和获取操作和顺序表不一样,因此需要重新编写相对应的函数:

bool link_list_insert(LinkList* list, int i, const LinkListNode node)
{
    bool ret = true;
    link_list_def* obj = (link_list_def*)list;
    if(obj && (i >= 0) && (i <= obj->base.length) && node)
    {
        struct link_list_node* current = position(obj, i);
        if(current)
        {
            ((struct link_list_node*)node)->next = current->next;
            current->next = node;
            ++obj->base.length;
        }        
    }
    else
    {
        ret = false;
    }
    return ret;
}
LinkListNode link_list_remove(LinkList* list, int i)
{
    LinkListNode ret = NULL;
    link_list_def* obj = (link_list_def*)list;
    if(obj && (i >= 0) && (i < obj->length))
    {
        struct link_list_node* current = link_list_position(obj, i);
        struct link_list_node* next = (current) ? (current->next) : (NULL);
        if(current && next)
        {
            ret = next;
            current->next = next->next;
            --obj->length;
        }        
    }
    return ret;
}
bool link_list_get(LinkList* list, int i, LinkListNode* node)
{
    bool ret = true;
    link_list_def* obj = (link_list_def*)list;
    if(obj && (i >= 0) && (i < obj->length) && node)
    {
        struct link_list_node* current = position(obj, i);
        *((struct link_list_node**)node) = current->next;
    }
    else
    {
        ret = false;
    }
    return ret;
}

四、C语言实现多态

C++中如果一个类中存在虚函数,那么产生的对象都会有一个虚表指针,虚表指针位于对象的最前面,这个虚表指针指向虚函数表,虚函数表在构造函数中初始化,在析构函数中销毁,所以在这两个函数中都不会发生多态行为。

4.1 基类中增加虚表指针

上面的链表继承自顺序表,显然这种继承方式并不好,因此可以将其进行重构,首先定义一个接口 list_vtable_def,里面包含了顺序表和链表的方法,但是并没有具体实现,相当于C++中的纯虚类。

除了相同的接口之外,他们还有一些相同的属性,这些属性用变量来进行表示,在顺序表和链表中,表的长度就是一种属性,由此在派生出一个抽象类 list_def。

C语言如果要实现多态也可以参考C++的内部实现,在基类中增加一个成员虚表指针。
而刚才所说的 list_vtable_def就是一个虚函数表的定义,派生出的每一个对象都会有一个虚表指针,指向对应类的虚函数表,虚表在构造函数中初始化,在析构函数中销毁。

typedef struct
{
    bool (*insert)(List* list, int i, const ListNode node);
    ListNode (*remove)(List* list, int i);
    bool (*get)(List* list, int i, ListNode* node);
    int (*length)(List* list);
}list_vtable_def;

typedef struct
{
    list_vtable_def* vtable;
    int length;
}list_def;

定义好了抽象类之后,前面两个类的定义也需要进行修改:

typedef struct
{
    list_def base;
    VectorNode* head;
}vector_def;

typedef struct
{
    list_def base;
    struct link_list_node head;
}link_list_def;

4.2 虚表的构建和初始化

除了需要定义虚函数表之外,每一个不同的类都需要定义一个不同的虚表,将虚表指针指向它,下面来看一下顺序表和链表中的虚表以及他们的构造:

// 定义在vector.c中的虚表
static vector_vtable s_vector_vtable = {
    .insert = vector_insert,
    .remove = vector_remove,
    .get = vector_get,
    .length = vector_length
};
// 构造函数
Vector* vector_create(int length)
{
    vector_def* ret = NULL;

    if(length > 0)
    {
        ret = malloc(sizeof(vector_def));
        ret->head = malloc(sizeof(VectorNode) * length);
        if(ret && ret->head)
        {
            ret->base.vtable = &s_vector_vtable;
            ret->base.length = length;
            for(int i = 0; i < length; i++)
            {
                ret->head[i] = NULL;
            }
        }
    }
    return (Vector*)ret;
}
// 定义在link_list.c中的虚表
static vector_vtable s_link_list_vtable = 
{
    .insert = link_list_insert,
    .remove = link_list_remove,
    .get = link_list_get,
    .length = vector_length
};
// 构造函数
LinkList* link_list_create()
{
    link_list_def* list = malloc(sizeof(link_list_def));
    if(list)
    {
        list->base.length = 0;
        list->base.vtable = &s_link_list_vtable;
        list->head.next = NULL;
    }
    return (LinkList*)list;
}

4.3 利用虚表实现多态

前面定义了虚函数,利用虚函数可以实现多态,对外提供一个统一的接口供应用层调用:

typedef void List;
typedef void* ListNode;

void list_insert(List* list, int i, ListNode node)
{
    if((*((list_vtable_def**)list))->insert)
    {
        (*((list_vtable_def**)list))->insert(list, i, node);
    }
    else
    {
        LOG(ERR_CONSTRUCT(NullPointer), "insert function not exist in vtable");
    }
}

ListNode list_remove(List* list, int i)
{
    ListNode ret = NULL;
    if((*((list_vtable_def**)list))->remove)
    {
        ret = (*((list_vtable_def**)list))->remove(list, i);
    }
    else
    {
        LOG(ERR_CONSTRUCT(NullPointer), "remove function not exist in vtable");
    }
    return ret;
}

ListNode list_get(List* list, int i)
{
    ListNode ret = NULL;
    if((*((list_vtable_def**)list))->get)
    {
        (*((list_vtable_def**)list))->get(list, i, &ret);
    }
    else
    {
        LOG(ERR_CONSTRUCT(NullPointer), "get function not exist in vtable");
    }
    return ret;
}

int list_length(List* list)
{
    if((*((list_vtable_def**)list))->length)
    {    
        return (*((list_vtable_def**)list))->length(list);
    }
    else
    {
        LOG(ERR_CONSTRUCT(NullPointer), "length function not exist in vtable");
        return 0;
    }
}

实现完成之后分别创建一个链表和一个顺序表,然后调用上面的四个函数:

#include "err.h"
#include "Vector.h"
#include "LinkList.h"

#define MAX_LEN 10

struct list_node
{
    struct link_list_node next;
    int data;
};
// 100个链表节点
struct list_node  array1[MAX_LEN] = {0};
// 100个顺序表节点
int array2[MAX_LEN];

int main()
{
    LinkList* list = link_list_create();
    Vector* vector = vector_create(MAX_LEN);
    
    // 将数据插入表中
    for(int i = 0; i < MAX_LEN; i++)
    {
        array1[i].data = MAX_LEN - i;
        array2[i] = i;
        list_insert(list, i, &array1[i]);
        list_insert(vector, i, &array2[i]);
    }
    printf("list length:%d\n", list_length(list));
    printf("vector length:%d\n", list_length(vector));

    // 输出链表中的值
    printf("list:");
    for(int i = 0; i < MAX_LEN; i++)
    {
        struct list_node* ret = list_get(list, i);
        printf("%d ", ret->data);
    }
    printf("\n");

    // 输出顺序表中的值
    printf("vector:");
    for(int i = 0; i < MAX_LEN; i++)
    {
        int* ret = list_get(vector, i);
        printf("%d ", *ret);
    }
    printf("\n");

	//移除链表中的数据
    while(list_length(list))
    {
       list_remove(list, 0);
    }
    printf("list length:%d\n", list_length(list));
    printf("vector length:%d\n", list_length(vector));
    
    vector_clear(vector);
    link_list_clear(list);
}

最终的结果

$> ./a.out
list length:10
vector length:10
list:10 9 8 7 6 5 4 3 2 1 
vector:0 1 2 3 4 5 6 7 8 9 
list length:0
vector length:10

4.4 代码仓库

仓库

  • 21
    点赞
  • 159
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咕咚.萌西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值