老汤回味——C语言与面向对象编程
今天的文章我们来看看如何结合面向对象的思想使用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
![](/assets/blank.gif)
老汤回味——C语言与面向对象编程相关推荐
- C++学习笔记:(七)C语言实现面向对象编程
面试官:C和C++有什么不同? 应聘者:一个是面向过程,一个是面向对象. 这样的答案在我看是不完全正确,因为面向过程编程和面向对象编程是编程思想,C++可以用面向过程的思想编程,同样,C语言也可以用面 ...
- C 语言实现面向对象编程
C 语言实现面向对象编程 1.引言 面向对象编程(OOP)并不是一种特定的语言或者工具,它只是一种设计方法.设计思想.它表现出来的三个最基本的特性就是封装.继承与多态.很多面向对象的编程语言已经包含这 ...
- 一步步分析-C语言如何面向对象编程
这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...
- ctor c语言,一步步分析-C语言如何面向对象编程
这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...
- c语言实现面向对象编程
文章目录 概述 框架介绍 基类 c++基类 c 实现基类 派生类 c++派生类 C实现派生类 基于模型的多重继承 C++的多重继承 c的多重继承 对象创建和使用 C++ 应用 C应用 概述 有一种 ...
- Java语言基础-面向对象编程三步走之打开冰箱门
开头: 何谓"面向对象" 面向对象是一种编程思想. 思想是一个很虚无缥缈的东西,但是它可以从一个人的具体行动中体现出来,如果说你坚持每天跑步,每天读书,做一些有益于身心健康的事情, ...
- PHP语言之面向对象编程
第3关:面向对象编程与数据库综合运用 <?php class model {protected $db; //数据库操作类实例protected $tableName; //待操作的表名prot ...
- java语言基础-面向对象编程-方法(廖雪峰老师官网学习记录)
Java是一种面向对象的编程语言. 面向对象编程,英文是Object-Oriented Programming,简称OOP. 面向对象编程,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方 ...
- C语言中面向对象编程
C语言中面相对象的编程 面向对象的重要思想就是数据隐藏,在面向对象语言中,对象可以包含私有变量.这样我们可以说他们具有内部状态,这些内部状态对其他对象是透明的.全局变量可以通过设置变量作用域来模拟私有 ...
最新文章
- linux 下 使用wget 下载 jdk资源 命令
- pycharm同一目录下无法import其他文件
- USB接口芯片的选型参考(Z)
- 【TensorFlow-windows】name_scope与variable_scope
- 集成框架比较– Spring集成,Mule ESB或Apache Camel
- 监督学习 | SVM 之非线性支持向量机原理
- B1295 [SCOI2009]最长距离 最短路
- uni-app的事件传参
- Linux 超全实用指令大全 | CSDN 博文精选
- 无人驾驶、人脸识别,这些牛X哄哄的技术都是怎么实现的?
- 执行对象cocos2d-x 2.x action动作整理集合
- 前端工程师的摸鱼日常(4)
- ezw证件照芯片压缩算法
- 德国精品软件 小红伞杀毒软件 AntiVir
- 微信转发后链接中不显示缩略图
- java做五子棋 视频_java swing实现五子棋小游戏项目源码附带视频指导运行教程...
- python 通达信选股_大智慧公式转python,使用python在通达信里面选股
- 用python实现打字练习网站第一
- centos配置ipv6
- 给睡猫的学习计划(转载) by 冰血封情