今天的文章我们来看看如何结合面向对象的思想使用C语言写出结构良好的代码。直接看代码,然后我们来分析一下代码中的含义。首先是头文件user.h:

#ifndef USER_H
#define USER_H#define USERNAME_LEN 255
#define PASSWORD_LEN 255typedef struct {char username[USERNAME_LEN];char password[PASSWORD_LEN];
} USER, *PUSER;PUSER init_user(char *username, char *password);char *get_username(PUSER puser);
void set_username(PUSER puser, char *username);int check_password(PUSER puser, char *check_pwd);
void set_password(PUSER puser, char *pwd);void destory_user(PUSER puser);#endif

最上面我们定义了一个结构体代表用户类,但是结构体中的成员变量只能是基本类型,数组或者指针,没有办法定义类方法,我们该怎么办呢?看下面的方法定义,init_user相当于我们的构造函数,传入username和password,init_user返回一个创建好的用户类对象的指针,而destory_user则类似于析构函数。

中间我们还定义了一些属性的getter和setter方法,可以看到,第一个参数是我们要操作的对象,也就是消息的接收者,是一个用户类对象的指针,它起到的作用类似于我们在一些面向对象语言中的this。

接下来具体看一下函数的实现,在user.c中,首先,我们声明了一个静态函数

static int is_user_valid(PUSER puser);

之前说过,静态函数的作用于为文件作用域,也就是说,这个函数的作用域仅限于user.c文件,而我们之前的user.h文件中并没有声明该函数,所以,我们可以理解为,这个函数是一个私有函数,只在我们的用户类内部使用。该函数定义如下:

static int is_user_valid(PUSER puser) {if (NULL == puser) {return 0;} else {return 1;}
}

简单起见,我这里只是校验了puser是否为NULL,还可以在这个函数中添加其他用户有效性校验,比如校验该用户是否是我们创建并记录在案的等等。

接下来看看我们的公有函数,也就是在user.h中声明的函数

PUSER init_user(char *username, char *password) {if (NULL == username || NULL == password) {printf("init_user error: username or password is NULL\n");return NULL;}PUSER puser = (PUSER)malloc(sizeof(USER));memset(puser, 0, sizeof(USER));strncpy(puser->username, username, USERNAME_LEN - 1);strncpy(puser->password, password, PASSWORD_LEN - 1);return puser;
}

init_user函数创建一个用户对象,开始我们进行了入参校验,然后使用malloc动态分配了空间,之后初始化属性字段。

void destory_user(PUSER puser) {if (!is_user_valid(puser)) {return;}free(puser);puser = NULL;
}

destory_user函数销毁对象,首先入参校验,之后free内存空间,将用户指针置为NULL,这是C语言动态内存释放常用的手段。

char *get_username(PUSER puser) {if (!is_user_valid(puser)) {return "";}return puser->username;
}void set_username(PUSER puser, char *username) {if (!is_user_valid(puser) || NULL == username) {return;}memset(puser->username, 0, USERNAME_LEN);strncpy(puser->username, username, USERNAME_LEN - 1);
}int check_password(PUSER puser, char *check_pwd) {if (!is_user_valid(puser) || NULL == check_pwd) {return 0;}return 0 == strncmp(puser->password, check_pwd, PASSWORD_LEN);
}void set_password(PUSER puser, char *pwd) {if (!is_user_valid(puser) || NULL == pwd) {return;}memset(puser->password, 0, PASSWORD_LEN);strncpy(puser->password, check_pwd, PASSWORD_LEN - 1);
}

其他四个函数比较简单,大家自己看一下,注意入参校验及使用字符串安全函数进行操作。最后,是我们的main.c

#include <stdio.h>
#include "user.h"int main(void) {PUSER puser = init_user("yjp", "123456");if (NULL == puser) {printf("init user error!\n");return 1;}printf("init username: %s\n", get_username(puser));printf("change username\n");set_username(puser, "yjp1");printf("now username: %s\n", get_username(puser));printf("check password: %d\n", check_password(puser, "123456"));printf("change password\n");set_password(puser, "654321");printf("now check password: %d\n", check_password(puser, "123456"));destory_user(puser);return 0;
}

执行结果如下:

init username: yjp

change username

now username: yjp1

check password: 1

change password

now check password: 0

从上面的代码不难看出,使用C语言的语言机制可以写出结构很好的代码,清晰简洁,封装也很到位。

接下来再思考一个问题,如果我们想提供一个接口,允许模块的使用者使用自己的密码加密方式该怎么办?如果了解设计模式,能够想到,这里我们使用模板方法模式。下面看看使用C语言如何实现。首先user.h中添以下内容:

typedef char* (*PWD_DEAL_FUNC)(char *pwd);
typedef struct {PWD_DEAL_FUNC pwd_deal;
} USER_OPERATIONS, *PUSER_OPERATIONS;void user_ops_register(PUSER_OPERATIONS ops);

定义一个函数指针类型,该函数指针指向的函数以一个字符串为参数返回一个字符串,定义一个结构体代表用户操作接口,可以看到,结构体可以将函数指针作为成员变量,只有函数指针的结构体,我们可以将其当做其他面向对象语言中的接口或者抽象类。之后声明了一个注册用户操作的函数。下面看一下user.c中的调整:

static PUSER_OPERATIONS g_user_ops = NULL;
void user_ops_register(PUSER_OPERATIONS ops) {if (NULL == ops) {return;}g_user_ops = ops;
}

首先定义一个全局的用户操作对象指针,然后实现了注册方法。如果在并发环境下执行,这里应当考虑全局变量的共享问题,这里简化问题,不去过多说明。与密码相关的两个方法作出修改:

int check_password(PUSER puser, char *check_pwd) {if (!is_user_valid(puser) || NULL == check_pwd) {return 0;}char *dealed_pwd = check_pwd;if (NULL != g_user_ops) {dealed_pwd = g_user_ops->pwd_deal(dealed_pwd);if (NULL == dealed_pwd) {return 0;}}printf("check password is %s\n", dealed_pwd);return 0 == strncmp(puser->password, dealed_pwd, PASSWORD_LEN);
}void set_password(PUSER puser, char *pwd) {if (!is_user_valid(puser) || NULL == pwd) {return;}char *dealed_pwd = pwd;if (NULL != g_user_ops) {dealed_pwd = g_user_ops->pwd_deal(pwd);if (NULL == dealed_pwd) {return;}}memset(puser->password, 0, PASSWORD_LEN);strncpy(puser->password, dealed_pwd, PASSWORD_LEN - 1);printf("password set to %s\n", puser->password);
}

在赋值和检查密码前都对密码使用模板方法,也就是调用我们注册的密码处理操作函数。main.c改动:

#include <string.h>
#include <stdio.h>
#include "user.h"static char *password_deal(char *pwd) {if (NULL == pwd) {return NULL;}if (0 == strncmp("654321", pwd, PASSWORD_LEN)) {return "123456";}return pwd;
}static USER_OPERATIONS g_user_ops = {.pwd_deal = password_deal
};int main(void) {user_ops_register(&g_user_ops);......return 0;
}

我们的密码处理就是如果要设置为654321,就将其改变为123456。在main开始,对我们的操作接口进行了注册。上面的结构体初始化使用了字段初始化。执行结果为:

init username: yjp

change username

now username: yjp1

check password is 123456

check password: 1

change password

password set to 123456

check password is 123456

now check password: 1

通过今天的实践可以看到,面向对象编程思想具有普适性,作为编程思想,可以用任何语言加以实现,面向对象语言,只是在语法层面上提供了面向对象编程的支持,方便我们写出面向对象的代码,但是像C这样的编程语言,依然可以把面向对象思想作为我们编写代码的武器,这样写出的C语言代码十分工整,也易于扩展和维护。

对于C语言的学习,就告一段落了,个人认为,学习C语言最好的方式,可以去学习Linux内核代码,去看看庞大的内核代码中C语言使用的亮点,Linux内核代码很好的展现了C语言的博大精深,代码结构也很好,非常值得了解。

代码已上传至github

https://github.com/yjp19871013/c_study

老汤回味——C语言与面向对象编程相关推荐

  1. C++学习笔记:(七)C语言实现面向对象编程

    面试官:C和C++有什么不同? 应聘者:一个是面向过程,一个是面向对象. 这样的答案在我看是不完全正确,因为面向过程编程和面向对象编程是编程思想,C++可以用面向过程的思想编程,同样,C语言也可以用面 ...

  2. C 语言实现面向对象编程

    C 语言实现面向对象编程 1.引言 面向对象编程(OOP)并不是一种特定的语言或者工具,它只是一种设计方法.设计思想.它表现出来的三个最基本的特性就是封装.继承与多态.很多面向对象的编程语言已经包含这 ...

  3. 一步步分析-C语言如何面向对象编程

    这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...

  4. ctor c语言,一步步分析-C语言如何面向对象编程

    这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...

  5. c语言实现面向对象编程

    文章目录 概述 框架介绍 基类 c++基类 c 实现基类 派生类 c++派生类 C实现派生类 基于模型的多重继承 C++的多重继承 c的多重继承 对象创建和使用 C++ 应用 C应用 概述   有一种 ...

  6. Java语言基础-面向对象编程三步走之打开冰箱门

    开头: 何谓"面向对象" 面向对象是一种编程思想. 思想是一个很虚无缥缈的东西,但是它可以从一个人的具体行动中体现出来,如果说你坚持每天跑步,每天读书,做一些有益于身心健康的事情, ...

  7. PHP语言之面向对象编程

    第3关:面向对象编程与数据库综合运用 <?php class model {protected $db; //数据库操作类实例protected $tableName; //待操作的表名prot ...

  8. java语言基础-面向对象编程-方法(廖雪峰老师官网学习记录)

    Java是一种面向对象的编程语言. 面向对象编程,英文是Object-Oriented Programming,简称OOP. 面向对象编程,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方 ...

  9. C语言中面向对象编程

    C语言中面相对象的编程 面向对象的重要思想就是数据隐藏,在面向对象语言中,对象可以包含私有变量.这样我们可以说他们具有内部状态,这些内部状态对其他对象是透明的.全局变量可以通过设置变量作用域来模拟私有 ...

最新文章

  1. linux 下 使用wget 下载 jdk资源 命令
  2. pycharm同一目录下无法import其他文件
  3. USB接口芯片的选型参考(Z)
  4. 【TensorFlow-windows】name_scope与variable_scope
  5. 集成框架比较– Spring集成,Mule ESB或Apache Camel
  6. 监督学习 | SVM 之非线性支持向量机原理
  7. B1295 [SCOI2009]最长距离 最短路
  8. uni-app的事件传参
  9. Linux 超全实用指令大全 | CSDN 博文精选
  10. 无人驾驶、人脸识别,这些牛X哄哄的技术都是怎么实现的?
  11. 执行对象cocos2d-x 2.x action动作整理集合
  12. 前端工程师的摸鱼日常(4)
  13. ezw证件照芯片压缩算法
  14. 德国精品软件 小红伞杀毒软件 AntiVir
  15. 微信转发后链接中不显示缩略图
  16. java做五子棋 视频_java swing实现五子棋小游戏项目源码附带视频指导运行教程...
  17. python 通达信选股_大智慧公式转python,使用python在通达信里面选股
  18. 用python实现打字练习网站第一
  19. centos配置ipv6
  20. 给睡猫的学习计划(转载) by 冰血封情

热门文章

  1. Locally Linear Emding(LLE降维) 算法讲解及Python 实现
  2. 几种常用用户行为分析模型简述
  3. 2017新年英文好歌极力推荐《Raindrops》——中文《雨滴》
  4. Oracle rman备份级别,RMAN备份等级详解
  5. 收藏个不错的图片处理网站
  6. 硬件可信计算环境 计算困难性理论 对比
  7. ASEMI代理ADV7611BSWZ原装ADI车规级ADV7611BSWZ
  8. 《时空穿越者》--失败男人的幻觉
  9. Java基础 - 面板组件JPanel
  10. 强化学习 折扣率_强化学习中的折扣因素的惩罚