栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言实现面向对象

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言实现面向对象

文章目录
  • 一、面向对象的三个基本特征
    • 1.1 封装
    • 1.2 继承
    • 1.3 多态
  • 二、C语言实现封装
    • 2.1 成员变量定义和访问控制
    • 2.2 对象的创建和删除
    • 2.3 成员函数的访问控制
  • 三、C语言实现继承
    • 3.1 子类继承父类成员变量
    • 3.2 子类使用父类成员函数
  • 四、C语言实现多态
    • 4.1 基类中增加虚表指针
    • 4.2 虚表的构建和初始化
    • 4.3 利用虚表实现多态
    • 4.4 代码仓库

一、面向对象的三个基本特征 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;
}

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;
}

与公有成员函数相对应的还有私有成员函数,不能被外部进行访问,这时可以使用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("%dn", 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;
}

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->base.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 基类中增加虚表指针

C语言如果要实现多态也可以模拟C++的内部实现,在基类中增加一个成员虚表指针。

上面已经介绍了顺序表和链表中的插入和获取的不同实现,因此这两个函数应该是虚函数表中的成员:

typedef struct
{
    bool (*insert)(Vector* list, int i, const VectorNode node);
    bool (*get)(Vector* list, int i, VectorNode* node);
}vector_vtable;

typedef struct
{
	vector_vtable* vtable;
    int length;
    VectorNode* head;
}vector_def;
4.2 虚表的构建和初始化

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

// 定义在vector.c中的虚表
static vector_vtable s_vector_vtable = {
    .insert = vector_insert,
    .get = vector_get
};
// 构造函数
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->vtable = &s_vector_vtable;
            ret->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,
    .get = link_list_get
};
// 构造函数
linkList* link_list_create()
{
    link_list_def* list = malloc(sizeof(link_list_def));
    if(list)
    {
        list->base.length = 0;
        list->base.head = NULL;
        // 初始化虚表
        list->base.vtable = &s_link_list_vtable;
        list->head.next = NULL;
    }
    return (linkList*)list;
}
4.3 利用虚表实现多态
#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];

// 读取数据,最前面四个字节是虚表指针,通过虚表实现多态行为
VectorNode read_data(Vector* list, int i)
{
    VectorNode ret = NULL;
    // 使用各自的函数将数据进行获取
    (*((vector_vtable**)list))->get(list, i, &ret);
    return ret;
}

// 写入数据
void write_data(Vector* list, int i, VectorNode node)
{
    (*((vector_vtable**)list))->insert(list, i, node);
}

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;
        write_data(list, i, &array1[i]);
        write_data(vector, i, &array2[i]);
    }

    printf("list length:%dn", vector_length(list));
    printf("vector length:%dn", vector_length(vector));

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

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

    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
4.4 代码仓库

仓库

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/664657.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号