2014年8月25日,收藏家和杀手——面向对象的C++和C(一)

最近事情特别多,睡眠也都很晚,有点精神和身体混乱的感觉,所以想写写技术分析文章,让两者的我都调整一下。这篇技术分析文章是一直想写的,当前只是开篇,有感觉的时候就写写,属于拼凑而成,后续的篇章没有时间计划,随缘吧。

收藏家和杀手——面向对象的C++和C(一)

=========

用了至少12年的C++,前些年Linux之父Linus在批评C++的时候(具体可看CSDN的《C++一无是处》所提到的这起事件:http://www.csdn.net/article/a/2010-06-12/218785),引起了全世界很多资深程序员发起声讨C++的群体骚动,我虽然没有骚动,但还是跟在后面看了他们的大部分描述,我得承认,他们说的大部分是事实(其实每个语言都有很多可以批判的事实)。

作为一名有强迫症的程序员(我坚信有强迫症的程序员才是好程序员),长期的C++经验让我在项目伊始就一直就纠缠在Code Style,Design Pattern、Framework等因素的形式是否优美、扩展性是否周到等问题,这些问题其实就是内心反复自我怀疑式的噩梦,更糟糕的是这个噩梦的时间占用的项目周期还不少,虽然我个人也很清楚,这些噩梦其实和项目的最后目的没有直接关系(不会被老板、客户、用户看到),而且也清楚噩梦的时间越长,就越和敏捷(Agile)的原型迭代流程以及精益(Lean Startup)思想背道而驰,但还似乎忍不住……这甚至类似于一种心理疾病。

相信很多C++老程序员都有和我相同的感觉,也许这个噩梦的起因可以解释为是因为我们都太想在编程中寻找一种完美和纯粹,但商业项目并不看重内里的完美和纯粹,换句话说就是我们想把艺术性的行为表现在代码中,让编程艺术化,但商业项目说“没必要,能用就是好的”。

对于这个噩梦还有一种更客观的解释,就是C++给了我们太多而且相互矛盾的选择(Java给我们的选择也很多,但矛盾少了,组织的好一点),而让我们这些强迫症患者无所适从,而商业项目只要实现,才不管你怎么选择。更通俗来说,就像打架,C++给我们了很多武器,太多的选择和矛盾组合让我们无所适从,能够使用好这么多武器需要成年累月的经验,而商业项目就是最快最准的打倒对方,才不管你用啥。想想看,当我拥有匕首、手枪、机关枪、火箭炮……时,我很可能会忘了我的目的是打到对方,而当起了武器收藏家;但当我只有匕首在手的时候呢?也许对方真正要倒霉了——我很可能是个抱着誓死决心的杀手。

——所以有时候,少就是多,多就是少:)

从看到Linus对C++的批判开始,我就一直想是否可以只用C来写某个新项目,体验一下和C++写程序的不同,可惜后续好几个Linux的后台并发项目都还是用C++写的。直到最近一个项目,是涉及232和485接口的硬件传感器的Linux项目,才让我下决心全部用C来写。下面就来说说我的一些感想和总结。

杀手也可能成为收藏家——C也可以写基础的面向对象

=========

也许在看到C可以写面向对象这句话后,很多初、中级程序员惊呆了,认为我胡说八道。如果我换个说法,C可以以面向对象的方式写程序,但C语言本身并不直接具有面向对象的特性,这些程序员也许就会好受一些,会从认为我胡说八道转为认为我有机会自圆其说。下面就让我自圆其说一下。

就语言本身而言,C语言的确不是一个面向对象的语言,但我们的问题是,那么C语言可以实现面向对象编程吗?我的答案是可以。面向对象就基本本质而言,没有那么复杂,其实就是对数据和操作这些数据的方法的统一封装。C++中有class(或者struct)这么一个关键字可以把数据和关联的方法封装成一个类,那么C语言呢?C语言的struct其实也可以把数据和关联的方法封装在一起。也许很多人说,你骗人,C语言的struct中只能放成员变量,不能放成员方法,也许你忘了还有函数指针这么一个东西,这个东西的存在,可以把方法像变量一样的放在C语言的struct中,如下(以下三个代码文件在Mac
OS X的XCode上完成):

//

//  person.h

//  cthinking

//

//  Created by Rafael Gu on 14-8-25.

//  Copyright (c) 2014年 Rafael Gu. All rights reserved.

//

#ifndef _PERSON_H_

#define _PERSON_H_

struct person;

typedef unsigned char (*GET_AGE)(struct person *this);

typedef void (*SET_AGE)(struct person *this, unsigned char age);

// all public members

struct person {

char name[32];

GET_AGE get_age;

SET_AGE set_age;

};

struct person *person_create();

void person_destroy(struct person *p);

#endif // _PERSON_H_

//

//  person.c

//  cthinking

//

//  Created by Rafael Gu on 14-8-25.

//  Copyright (c) 2014年 Rafael Gu. All rights reserved.

//

#include "person.h"

#include <stdlib.h>

// all private members

struct _person {

unsigned char age;

};

static unsigned char _get_age(struct person *this)
{

struct _person *p = (struct _person *)(this
+ 1);

return p->age;

}

static void _set_age(struct person *this, unsigned char age)
{

struct _person *p = (struct _person *)(this
+ 1);

p->age = age;

}

struct person *person_create() {

struct person *p = (struct person *)malloc(sizeof(struct person)
+ sizeof(struct _person));

p->get_age = _get_age;

p->set_age = _set_age;

return p;

}

void person_destroy(struct person *p) {

free(p);

}

//

//  main.c

//  cthinking

//

//  Created by Rafael Gu on 14-8-25.

//  Copyright (c) 2014年 Rafael Gu. All rights reserved.

//

#include <stdio.h>

#include <string.h>

#include "person.h"

int main(int argc, const char *
argv[]) {

struct person *this = person_create();

memset(this->name, 0, 32);

strncpy(this->name, "rafael", 6);

this->set_age(this, 35);

printf("%s‘s age is: %u\n", this->name, this->get_age(this));

person_destroy(this);

return 0;

}

如果已经看懂上面代码的程序员,应该会会心一笑,上面主要用了函数指针、struct的变体偏移、编译单元的static函数(变量也同样)等C的技术。如果是写过objective-c的程序员,马上就会觉得和不同private和public关键字的objective-c的类结构很像。

在上面的类中,name是public变量,age是私有变量,通过对应的getter和setter访问。person_create可以看做是new和构造函数的结合体,而person_destroy可以看做是delete和析构函数的结合体。当然这里只实现了private和public,没有protected。

如果有人问,如何实现class的static函数和变量,其实他们就是C的编译单元的static函数和变量,只要看懂上面的代码,并且知道编译单元是啥以及和static什么关系就明白了。

今天就写到这里,下次如果再写,就像写高级一点的面向对象知识了,比如多态。当然为了防止没有下文的遗憾,我这里把实现多态的技术原理说一下:

  • 继承——你看上面的person和_person的struct变体偏移就知道如何组合不同结构体实现继承了。
  • 重载、虚函数——你看上面的函数指针在“构造”函数才被指定,那么你应该明白如何实现重载和虚函数了。

最后,这里要说一下,我只是在用C写面向对象,并不是要把C++或者Java的每个语言级的面向对象特性都展示出来,当然要展示,C也完全可以做得到。我的项目是基于上面代码的对象方式写得,如果完全实现C++或者Java的所有面向对象特性,也不太实用。

时间: 2024-11-03 21:51:18

2014年8月25日,收藏家和杀手——面向对象的C++和C(一)的相关文章

【每日圣经日历】2014年9月25日

Jeudi le 25 Septembre 2014 礼拜四 2014年9月25日 Si vous pardonnez aux hommes leurs offenses, votre Père céleste vous pardonnera aussi.                                                                      Matthieu 6. 14 你们饶恕人的过犯,你们的天父也必饶恕你们的过犯.            

郎咸平 马行空 - 郎咸平说:萧条下的希望(2014年10月25日)

<郎咸平说:萧条下的希望> 作 者:郎咸平 马行空译 者:系 列:出 版:东方出版社字 数:200千字阅读完成:2014年10月25日

DSR 系统开发记录(2014年7月25日):Hibernate User Type 更新操作

今天遇到的问题是在 VisitRecord 编辑保存之后,MedicineComment 中的 Medicine 属性就变成 null 了.经过简单的调查之后发现,在做更新操作时,起作用的并不是 UserType 的 nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) 方法,而是 Object replace(Object original, Object target, O

09.25日记(2014年9月25日23:22:06)用java这么多年面向对象我真的懂了吗,测试现行理念会玩吗

二胡 (1)应该找些书来看看,工作N年并不代表就有N年的工作经验. (2)DiaTransit02,DiaDept02,DiaAirport02,DiaHighway02.都具有x,y属性为何不设计一个DiaPoint类呢?! (3)周围认识的人工资都比我高了.zhouwei去宁波了搞自动化测试工具去了. (4)测试先行. (5)这是Groovy中文社区,太少内容了,而且也不是中文的啊. (6)R语言(维基).R语言中文网.R语言(官网) (7)Nutz问题列表

杭电OJ 2017 2014年9月25日21:05:42

字符串统计 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 41389    Accepted Submission(s): 23096 Problem Description 对于给定的一个字符串,统计其中数字字符出现的次数. Input 输入数据有多行,第一行是一个整数n,表示测试实例的个数,后面跟着n行,每行包括一个由字母和数字组成

杭电OJ 2016 2014年9月25日20:19:15

数据的交换输出 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 55551    Accepted Submission(s): 21128 Problem Description 输入n(n<100)个数,找出其中最小的数,将它与最前面的数交换后输出这些数. Input 输入数据有多组,每组占一行,每行的开始是一个整数n,表示这个测试实

杭电OJ 2015 2014年9月25日19:51:03

偶数求和 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 46061    Accepted Submission(s): 20130 Problem Description 有一个长度为n(n<=100)的数列,该数列定义为从2开始的递增有序偶数,现在要求你按照顺序每m个数求出一个平均值,如果最后不足m个,则以实际数量求平均值.编程输出

杭电OJ 2014 2014年9月25日19:09:13

青年歌手大奖赛_评委会打分 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 46823    Accepted Submission(s): 23468 Problem Description 青年歌手大奖赛中,评委会给参赛选手打分.选手得分规则为去掉一个最高分和一个最低分,然后计算平均得分,请编程输出某选手的得分. Input 输入数据

杭电OJ 2013 2014年9月25日18:59:38

蟠桃记 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 34943    Accepted Submission(s): 26317 Problem Description 喜欢西游记的同学肯定都知道悟空偷吃蟠桃的故事,你们一定都觉得这猴子太闹腾了,其实你们是有所不知:悟空是在研究一个数学问题!什么问题?他研究的问题是蟠桃一共有多少个!不