C语言模拟C++对函数和变量的封装

C++的方法和成员变量都是放在类里面的,通过类的实例化来访问其中的方法和成员,C++甚至还引入了命名空间,通过命名空间访问类,再实例化类。C语言也可以模拟C++的对类的封装,通过编码规范来实现,或者说,定了一套编码框架,按照这个框架来做,就可以实现封装。
先来看下面一段C代码。

/*mathOpr.c*/
#include<stdio.h>
int add(int data1, int data2)
{
    return data1 + data2;
}
int sub(int data1, int data2)
{
    return data1 - data2;
}

这段代码,定义了2个函数,add和sub,其他文件可以直接访问这2个函数,比如下面的代码。

/*main.c*/
#include <stdio.h>
#include "mathOpr.c"
int main(int argc, char *argv[])
{
    int ret = add(1, 2);
    printf("%d\n", ret);
    ret = sub(4, 3);
    printf("%d\n", ret);
    return 0;
}

gcc -o main main.c
./main
在这里插入图片描述
我现在要把mathOpr.c封装,再加个mathOpr.h文件。

.h文件

/*mathOpr.h*/
#ifndef __MATH_OPR_H
#define __MATH_OPR_H
#include <stdio.h>
/*封装函数,这2个函数也可以直接被调用*/
int add(int data1, int data2);
int sub(int data1, int data2);

/*定义封装函数结构体*/
typedef struct{
    int (*add)(int data1, int data2);
    int (*sub)(int data1, int data2);
} MathOprFunc;

/*封装函数变量*/
static MathOprFunc m_mathOprFunc = 
{
    .add = add,/*指向上面的add函数*/
    .sub = sub /*指向上面的sub函数*/
};
#endif

.c文件,在原来基础上,加上include上面的.h文件

/*mathOpr.c*/
#include<stdio.h>
int add(int data1, int data2)
{
    return data1 + data2;
}
int sub(int data1, int data2)
{
    return data1 - data2;
}

main.c文件,注意看,是#include “mathOpr.h”,而不是#include “mathOpr.c”

/*main.c*/
#include <stdio.h>
#include "mathOpr.h"
int main(int argc, char *argv[])
{
    int ret;
    ret = m_mathOprFunc.add(1, 2);/*通过封装变量m_mathOprFunc访问add函数*/
    printf("%d\n", ret);
    ret = m_mathOprFunc.sub(4, 3);/*通过封装变量m_mathOprFunc访问sub函数*/
    printf("%d\n", ret);
    return 0;
}

现在,不是直接编译成main可执行文件,而需要先把mathOpr.c编译成mathOpr.so,再把mathOpr.so作为main.c的编译文件。
#编译mathOpr.c
gcc -c mathOpr.c
#生成mathOpr.so
gcc -fpic -shared -o mathOpr.so mathOpr.o
#编译成main可执行文件
gcc -o main main.c mathOpr.so
#执行
./main
在这里插入图片描述
得到的结果和上面的一样。
为了实现封装,加了很多代码,而且编译过程也复杂了,感觉没必要进行封装了。但是,正如我当初学C语言,觉得C语言没啥用,1+1,我用计算器就可以算出来,没必要写那几行代码。当一个代码文件达到了数百行,甚至上千行,并且代码文件的函数有几十个,对函数和变量的封装就有必要了,有兴趣的可以用C++做同样的封装,代码量是差不多的。
一开始就提到,通过编码规范来实现封装,mathOpr.h里面的注释也提到,可以直接调用add和sub函数,万一其他代码文件也有类似功能的函数,命名也一样,那就会冲突了。所以,就通过命名规范(属于编码规范的内容)来区别,在所有的变量/结构体/函数前加上文件名称,在.h文件有体现,MathOprFunc和m_mathOprFunc就是,修改后的的.h文件和.c文件如下

/*mathOpr.h*/
#ifndef __MATH_OPR_H
#define __MATH_OPR_H
#include <stdio.h>
/*封装函数,这2个函数也可以直接被调用*/
int mathOprAdd(int data1, int data2);
int mathOprSub(int data1, int data2);
/*定义封装函数*/
typedef struct{
    int (*add)(int data1, int data2);
    int (*sub)(int data1, int data2);
} MathOprFunc;
/*封装函数变量,用add代替mathOprAdd,sub代替mathOprSub*/
MathOprFunc m_mathOprFunc = 
{
    .add = mathOprAdd,
    .sub = mathOprSub
};
#endif
/*mathOpr.c*/
#include<stdio.h>
int mathOprAdd(int data1, int data2)
{
    return data1 + data2;
}
int mathOprSub(int data1, int data2)
{
    return data1 - data2;
}

main.c不需要改动,此时,通过访问m_mathOprFunc.add来访问mathOprAdd函数,这里看到封装的痕迹了吧。
这种封装,会不会损耗性能呢,封装前是直接访问函数,封装后是通过变量来访问函数指针,达到访问函数的目的。可以直接的说,性能损耗是肯定的,因为多做了动作,但是,可以通过在m_mathOprFunc变量前加static,再编译时加-O3级别优化,性能是没有损失的,编译器的优化可以做到直接用mathOprAdd来替换m_mathOprFunc.add。同时,相对函数实现功能的耗时来说,访问函数的耗时微不足道,除非像这里的很简单的功能,有兴趣的可以写代码测试一下。
上面说的是封装函数,接下来是封装变量。
代码可能会用到全局静态变量,仅限于当前代码使用,比如static int giTmp;下面3个代码文件是示例
.h文件

/*mathOpr.h*/
#include <stdio.h>
/*定义封装变量结构体*/
typedef struct
{
    int data1;
    int data2;
} MathOprVar;
/*初始化封装变量*/
static inline MathOprVar mathOprVarInit()
{
    MathVar var = 
    {        
        .data1 = 10,
        .data2 = 20
    };
    return var;
}
/*封装函数,这2个函数也可以直接被调用*/
int mathOprAdd(MathOprVar *var);
int mathOprSub(MathOprVar *var);
/*定义封装函数*/
typedef struct
{    
    int (*add)(MathOprVar *var);
    int (*sub)(MathOprVar *var);
} MathOprFunc;
/*封装函数变量,用add代替mathOprAdd,sub代替mathOprSub*/
static MathOprFunc m_mathOprFunc = 
{    
    .add = mathOprAdd,
    .sub = mathOprSub,
};

.c文件

/*mathOpr.c*/
#include "mathOpr.h"
#include <stdio.h>
int mathOprAdd(MathVar *var)
{
    return var->data1 + var->data2;
}
int mathOprSub(MathVar *var)
{
    return var->data1 - var->data2;
}

main.c

#include "mathOpr.h"
#include <stdio.h>
int main(int argc, char *argv[])
{    
    MathOprVar var = mathOprVarInit();/*调用变量初始化函数*/
    printf("%d %d\n", m_mathOprFunc.add(&var), m_mathOprFunc.sub(&var));
    MathOprVar var2 = {12,23}; /*自己定义初始化的值*/
    printf("%d %d\n", m_mathOprFunc.add(&var2), m_mathOprFunc.sub(&var2));
    return 0;
}

从.h文件可以看到,多了结构体MathOprVar和函数mathOprVarInit。其中,结构体MathOprVar可以看作C++里面的类,而函数mathOprVarInit可以看作是构造函数,还可以加个mathOprRelease作为析构函数。
看main.c里面,可以用MathOprVar定义不同的变量,而且操作起来互不影响,因为,都是从参数传入的,并且操作的都是参数变量。由于每个函数都必须传入MathOprVar变量,这个变量当作不同函数交流的媒介,可以当作全局变量用。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值