C语言面对对象设计模式汇编
面向对象发展到今天,已经出现了许许多多优秀的实践、方法和技术。很多的技术都能够有效的提高软件质量。IBM上的《面向对象软件开发和过程》系列文章对面对对象设计从如下层面进行了详细的介绍:代码是核心、 案例实战(上)、 案例实战(下)、 重用、优化代码的组织、 针对契约设计、 业务建模。
虽然文章中的案例实战或代码示例都是以Java、C++等面对对象语言为主,但是辅以一定的方法和工具,C语言一样能做出优秀的面对对象设计,因为软件设计的基本理念原则是相通的。C语言面对对象设计的主要难点在于,对于抽象、继承、泛型(模板)等面对对象设计手段,在语法层次上缺少直接的支持。
本文试图对如下四个主要的面对对象设计手段提供C语言的解决方案和示例。
- 抽象数据类型
- 对象继承(又叫实现继承)
- 接口继承
- 函数模板
抽象数据类型
抽象数据类型:方法A
要点:
a. 头文件只提供类型声明和接口声明
b. 类型定义和接口实现在.c中完成
c. 接口支持参数类型检查
/* stach.h */
#ifndef STACK_H
#define STACK_H typedef struct stack *stack; extern stack new(void);
extern void free(stack *stk);
extern int empty(stack stk);
extern void push(stack stk, void *x);
extern void *pop(stack stk);
抽象数据类型:方法B
要点:
a. 头文件只提供接口声明
b. 定义一个 void * 指针类型全局变量,封装接口需要的所有和对象相关的信息
c. 类型定义和接口实现在.c中完成
d. 接口不支持参数类型检查
/* set.h */
#ifndef H
#define H //Set, Object 本质是对象size的指针,用于new一个新对象
extern const void * Set;
extern const void * Object; extern void * new (const void * type, ...);
extern void delete (void * item);
extern void * add (void * set, const void * element);
extern void * find (const void * set, const void * element);
extern void * drop (void * set, const void * element);
extern int contains (const void * set, const void * element);
extern unsigned count (const void * set); #endif /* main.c */
int main ()
{ void * s = new(Set);
void * a = add(s, new(Object));
void * b = add(s, new(Object));
void * c = new(Object); if (contains(s, a) && contains(s, b))
puts("ok"); if (contains(s, c))
puts("contains?"); if (differ(a, add(s, a)))
puts("differ?"); if (contains(s, drop(s, a)))
puts("drop?"); delete(drop(s, b));
delete(drop(s, c)); return 0;
}
对象继承
对象继承:方法A
要点:
a. 纯虚基类以聚合(关联)方式继承,类型为const void *以便于信息隐藏
b. 非纯虚基类以组合方式继承,类型为const struct superclass_name
c. 所有基类必须作为派生类第一个成员
d. 基类在派生类中以命名为'_'进行信息隐藏
e. 纯虚基类在各个派生类中实例化
f. 对外部不暴露具体数据类型
extern const void * Circle; /* new(Circle, x, y, rad) */
extern const void * Point; /* new(Point, x, y); */ void * new (const void * class, ...);
void delete (void * item);
void draw (const void * self);
void move (void * point, int dx, int dy); /* .c */
struct Class {
size_t size;
void * (* ctor) (void * self, va_list * app);
void * (* dtor) (void * self);
void (* draw) (const void * self);
}; struct Point {
const void * class;
int x, y;
}; struct Circle {
const struct Point _;
int rad;
}; void move (void * _self, int dx, int dy)
{ struct Point * self = _self; self -> x += dx, self -> y += dy;
} void draw (const void * self)
{ const struct Class * const * cp = self; assert(self && * cp && (* cp) -> draw);
(* cp) -> draw(self);
}
对象继承:方法B
要点:
a. 基类作为派生类的一个字段,但不必作为派生类的第一个字段
b. 通过 container_of 方法找到派生类的基类
c. 对外不暴露派生类类型,但暴露基类类型
d. 支持多重继承
e. 多用于业务逻辑/流程在派生类模块中实现的场景,基类为派生类提供公共服务
struct ep_device {
struct usb_endpoint_descriptor *desc;
struct usb_device *udev;
struct device dev;
};
#define to_ep_device(_dev) \
container_of(_dev, struct ep_device, dev)
对象继承:方法C
要点:
a. 派生类作为基类的一个字段,此字段以void *类型定义
b. 在派生类模块中对基类此字段进行初始化
c. 对外不暴露派生类类型,但暴露基类类型
d. 多用于业务逻辑/流程在基类模块中实现的场景,派生类为基类提供个性化服务
struct super_block {
/* omitted */
void *s_fs_info; /* Filesystem private info */
/* omitted */
}; int autofs_fill_super(struct super_block *s, void *data, int silent)
{
struct autofs_sb_info *sbi;
/* omitted */
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
goto fail_unlock; s->s_fs_info = sbi;
/* omitted */
}
接口继承
接口继承:虚函数表实现
要点:
a. 派生接口对象通过字段inherits继承基接口对象。
b. 派生接口对象通过ata_finalize_port_ops初始化。
c. C99中,若数据结构体中的某个字段被重复初始化,最后一次初始化有效。根据这个特点,实现接口成员的覆盖功能。
struct ata_port_operations {
/* omitted */
void (*sff_dev_select)(struct ata_port *ap, unsigned int device);
/* omitted */ /*
* ->inherits must be the last field and all the preceding
* fields must be pointers.
*/
const struct ata_port_operations *inherits;
}; const struct ata_port_operations ata_base_port_ops = {
.prereset = ata_std_prereset,
.postreset = ata_std_postreset,
.error_handler = ata_std_error_handler,
}; const struct ata_port_operations sata_port_ops = {
.inherits = &ata_base_port_ops,
.qc_defer = ata_std_qc_defer,
.hardreset = sata_std_hardreset,
};
const struct ata_port_operations sata_pmp_port_ops = {
.inherits = &sata_port_ops,
.pmp_prereset = ata_std_prereset,
.pmp_hardreset = sata_std_hardreset,
.pmp_postreset = ata_std_postreset,
.error_handler = sata_pmp_error_handler,
}; static void ata_finalize_port_ops(struct ata_port_operations *ops)
{
static DEFINE_SPINLOCK(lock);
const struct ata_port_operations *cur;
void **begin = (void **)ops;
void **end = (void **)&ops->inherits;
void **pp;
if (!ops || !ops->inherits)
return;
spin_lock(&lock);
for (cur = ops->inherits; cur; cur = cur->inherits) {
void **inherit = (void **)cur;
for (pp = begin; pp < end; pp++, inherit++)
if (!*pp)
*pp = *inherit;
}
for (pp = begin; pp < end; pp++)
if (IS_ERR(*pp))
*pp = NULL;
ops->inherits = NULL; spin_unlock(&lock);
}
接口继承:宏实现
要点:
a. 基类接口对象用宏实现。
b. 派生接口对象通过直接内嵌基类接口对象宏来实现继承。
c. C99中,若数据结构体中的某个字段被重复初始化,最后一次初始化有效。根据这个特点,实现接口成员的覆盖功能。
d. 只适用于两层继承结构。
#define BCM_XXX_OPS .dwBoardId = 0xffff, \
.trunk_id_translate = NULL, \
.trunk_add_port = bcm_XXX_trunk_add_port, \
... \
.trunk_del_port = bcm_XXX_trunk_del_port BCM_OPERATIONS bcm_XXX_table[]=
{
{BCM_XXX_OPS}, /*default*/
{BCM_XXX_OPS, .dwBoardId = _BT_3G_USRUD,
.pre_config = bcm_XXX_pre_config,
.packet_pro_config = bcm_XXX_packet_pro_config},
{BCM_XXX_OPS, .dwBoardId = _BT_3G_HMPUE,
.pre_config = bcm_XXX_pre_config,
.packet_pro_config = bcm_XXX_packet_pro_config},
{_BT_3G_NULL,0},
};
函数模板
函数模板: 参数多态实现
要点:
a. 通用操作,如:构造函数、解析函数、比较函数、复制函数等
b. 函数模板参数以 void * 定义
c. 把相关模板函数转化为纯虚基类的成员函数
d. 模板函数基于纯虚基类上实现
e. 每个派生类继承纯虚基类,纯虚基类以关联方式继承,类型为const void *以便于信息隐藏
f. 所有基类必须作为派生类第一个成员,所以不支持多重继承方式
g. 派生类具体实现自己特定接口
/* new.h */
void * new (const void * class, ...);
void delete (void * item); void * clone (const void * self);
int differ (const void * self, const void * b); size_t sizeOf (const void * self); /* new.r */
struct Class {
size_t size;
void * (* ctor) (void * self, va_list * app);
void * (* dtor) (void * self);
void * (* clone) (const void * self);
int (* differ) (const void * self, const void * b);
}; /* new.c */
void * new (const void * _class, ...)
{ const struct Class * class = _class;
void * p = calloc(1, class -> size); assert(p);
* (const struct Class **) p = class; if (class -> ctor)
{ va_list ap; va_start(ap, _class);
p = class -> ctor(p, & ap);
va_end(ap);
}
return p;
} /* string.h */
extern const void * String; /* string.c */
struct String {
const void * class; /* must be first */
char * text;
}; static void * String_ctor (void * _self, va_list * app)
{ struct String * self = _self;
const char * text = va_arg(* app, const char *); self -> text = malloc(strlen(text) + 1);
assert(self -> text);
strcpy(self -> text, text);
return self;
} static const struct Class _String = {
sizeof(struct String),
String_ctor, String_dtor,
String_clone, String_differ
}; const void * String = & _String; /* app.c */ int main ()
{ void * a = new(String, "a");
//...
delete(a);
return 0;
}
函数模板: 宏实现
要点:
a. 函数定义成宏表达式,因为宏表达式不携带类型。当然这损失了代码可维护性。
b. 函数参数类型通过C关键词typeof获取
c. 变量读写尽量利用内存操作,因为内存操作可泛化具体数据类型,通过union和sizeof获取参数首址和长度。
#define WRITE_ONCE(x, val) \
({ \
union { typeof(x) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(x)) (val) }; \
__write_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
}) static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
default:
barrier();
__builtin_memcpy((void *)p, (const void *)res, size);
barrier();
}
}
函数模板: 函数指针实现
要点:
a. 构造调用服务对象,来封装函数及其参数信息,不同的函数通过服务id来识别
b. 通过可变参数实现不同服务的参数提取
#include <stdio.h>
#include <stdarg.h> struct call_svc {
long id;
long func;
long arg_num;
}; static int a(void)
{
printf("%s(void)\n", __FUNCTION__);
return 0;
} static int b(long arg1)
{
printf("%s(%ld)\n", __FUNCTION__, arg1);
return 0;
} static int c(long arg1, long arg2, char* str)
{
printf("%s(%ld, %ld, %s)\n", __FUNCTION__, arg1, arg2, str);
return 0;
} static struct call_svc svc[] = {
{0, (long)&a, 0},
{1, (long)&b, 1},
{2, (long)&c, 3},
}; static int call(long id, ...)
{
int svc_num = sizeof(svc)/sizeof(svc[0]);
int i ,ret;
va_list argptr; for(i=0; i<svc_num; i++)
{
if(id == svc[i].id)
break;
} if(i >= svc_num)
{
printf("unsupported svc id: %ld\n", id);
return -1;
} va_start(argptr, svc[i].arg_num); switch (svc[i].arg_num) {
case 0:
ret = ((int (*)())svc[i].func)();
break; case 1:
{
long v1 = va_arg( argptr, long);
ret = ((int (*)(long))svc[i].func)(v1);
}
break; case 3:
{
long v1 = va_arg( argptr, long);
long v2 = va_arg( argptr, long);
char *v3 = va_arg( argptr, char *);
ret = ((int (*)(long, long, char *))svc[i].func)(v1, v2, v3);
} break; default:
printf("unsupported svc param number. id: %ld, arg_num: %ld.\n", id, svc[i].arg_num);
break;
}
va_end(argptr); return ret;
} int main(void)
{
call(0);
call(1, 11);
call(2, 11, 22, "hello world"); return 0;
}
C语言面对对象设计模式汇编的更多相关文章
- python面对对象编程----2:__init__
面对对象编程估计我们最早接触到的就是__init__了,也就是实例的初始化处理过程: 1:来看看最基础的__init__ class Card(object): #抽象类Card,并不用于实例化 de ...
- Javascript面对对象. 第一篇
Javascript,有两个种开发模式: 1.函数式(过程化)2.面对对象(oop),面对对象语言有一个标志,就是类,而通过类可以创建任何多个属性和方法,而Ecmascript没有类的概念,因此它的对 ...
- Python面对对象相关知识总结
很有一段时间没使用python了,前两天研究微信公众号使用了下python的django服务,感觉好多知识都遗忘了,毕竟之前没有深入的实践,长期不使用就忘得快.本博的主要目的就是对Python中我认为 ...
- js面对对象编程
说到js,非常大一部分人会说我非常熟悉,在日常的web开发中经经常使用,那么你的js代码是符合面对对象思路的吗?那你会问我面向过程的js代码有什么不好吗?我的感受是面对对象的js编码更加简洁,降低了混 ...
- Java学习——面对对象的思想入门
本文是看过<head first Java>之后的一点感悟,写点东西帮忙以后回忆,Java目前在我的工作中用到还不多,而我又对面对对象的编程非常的感兴趣.曾经在MFC平台上写过 ...
- Python - 面对对象(基础)
目录 Python - 面对对象(基础) 一. 概述 二. 创建类和对象 三. 面向对象三大特征 封装 继承 多态 Python - 面对对象(基础) 一. 概述 面向过程:根据业务逻辑从上到下写垒代 ...
- Python - 面对对象(其他相关,异常处理,反射,单例模式,等..)
目录 Python - 面对对象(其他相关,异常处理,反射,等..) 一.isinstance(obj, cls) 二.issubclass(sub, super) 三.异常处理 1. 异常处理 2. ...
- Golang - 面对"对象"
目录 Golang - 面对"对象" 1. 简介 2. 匿名字段 3. 方法 4. 包和封装 5. 接口 4. 包和封装 5. 接口 Golang - 面对"对象&quo ...
- Python学习6——再谈抽象(面对对象编程)
1.对象魔法 在面对对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法. 使用对象而非全局变量以及函数的原因有多个,而最重要的好处不过以下几点: 多态:可对不同类型的对象 ...
随机推荐
- ORA-28000错误的原因及解决办法
当使用SQL*Plus登录时,Oracle数据库时提示“ORA-28000:帐号被锁定”. 导致出现改错误的原因是:在oracle database 11g中,默认在default概要文件中设置了“F ...
- C++回顾day03---<类型转换>
一:C++类型转换 (一)static_cast<>() 静态类型转换:基本类型可以转换但是指针类型不允许.可以进行隐式类型转换 double n=1.23 int m=static_ca ...
- Fiddler--QuickExec
QuickExec在Fiddler中提供了比较快捷的功能服务. 在QuickExec输入框中输入命令,能快速地得到想要的结果. 快捷键:打开Fiddler后,按“Alt+q”,可将光标定位到Quick ...
- 第十节:数据批注(DataAnnotationModel)和自定义验证(包括Model级别的验证)
一. 简介 写完上一个章节MVC中的常用特性,迫不及待将该系列补全,该章节主要介绍数据批注(也叫:注解). 一听[数据批注],好高大上的名字,但仔细一看,它们其实是[System.ComponentM ...
- [物理学与PDEs]第4章第2节 反应流体力学方程组 2.2 反应流体力学方程组形式的化约
1. 粘性热传导反应流体力学方程组 $$\beex \bea \cfrac{\rd \rho}{\rd t}&+\rho \Div{\bf u}=0,\\ \cfrac{\rd Z}{\rd ...
- jsonp简介
jsonp主要是利用script的跨域.简单点说就是像img,css,js这样的文件是跨域的,这也就是为什么我们能够利用cdn进行加速的原因.而且像js这样的文件,如果里面是一个自执行的代码,比如: ...
- eclipse下classes文件夹无法发布到tomcat的问题--tomcat发布慢的问题
=== 解决eclipse下classes文件夹无法发布到tomcat的问题_Nautilus_新浪博客http://blog.sina.com.cn/s/blog_484d8777010130n5. ...
- BootStrap分页教程
https://www.cnblogs.com/laowangc/p/8875526.html https://www.cnblogs.com/yinglunstory/p/6092834.html ...
- 微信小程序传递参数(字符串、数组、对象)
[转自燕歆波]感谢! //通过提供的JSON.stingify方法,将对象转换成字符串后传递 click:function(e){ var model = JSON.stringify(e.curre ...
- 3D Slicer中文教程(一)—下载及安装方法
3D Slicer是用于医学图像信息学,图像处理和三维可视化的开源软件平台. 通过国家卫生研究院和全球开发人员社区的支持,二十多年来,Slicer为医生,研究人员和公众提供了免费,强大的跨平台加工工具 ...