编程思想.docx

上传人:b****7 文档编号:10440071 上传时间:2023-02-11 格式:DOCX 页数:45 大小:89.18KB
下载 相关 举报
编程思想.docx_第1页
第1页 / 共45页
编程思想.docx_第2页
第2页 / 共45页
编程思想.docx_第3页
第3页 / 共45页
编程思想.docx_第4页
第4页 / 共45页
编程思想.docx_第5页
第5页 / 共45页
点击查看更多>>
下载资源
资源描述

编程思想.docx

《编程思想.docx》由会员分享,可在线阅读,更多相关《编程思想.docx(45页珍藏版)》请在冰豆网上搜索。

编程思想.docx

编程思想

 

编程思想

 

非程序员编著

 

讲述编程思想

浓缩代码精华

提笔写下编程思想的书名时,不禁莫名惆怅。

笔者历经7年苦读(这期间有大半时间在虚度光阴)和为了生计四年的打拼,终究也没能成为程序员。

写这本书绝不敢让程序员们指正,仅作为一名非程序员对程序员们的审视,只想给有志成为程序员的初学者作一点参考,交流一下思想。

在校7年由于资质愚钝及专业、条件、或许还有恋爱等限制,什么也未学到便毕业了,饶是课题美名其曰为“面向对象的仿真”之类(实在没作任何东西,故实不敢真名以示之),其实连C++的皮毛都不知道,当然这在某些院校也是可以毕业的,这不知说明了什么?

期间也曾去清华谋求深造,可惜清华学子对理论水平的要求实在太高,我大概学完本科便已啃不进太理论(而不是理论联系实际)的书籍,以致我没法迈进这门槛。

这里找诸多理由也就是给自己找个借口罢了,相信是金子终究会发光,革命尚未成功,因此诸君在校多努力啊!

,不过读博士什么的还请慎重。

毕业应聘到一家自动化公司就职,本想去软件公司、通讯公司什么的,可是技不如人啊。

到了自动化公司才发现所有的硬件均是鬼子的产品,软件呢鬼子已经为你准备好一套完整的图形组态开发系统(在此不得虚心承认鬼子的软件水平),于是乎从此放下了刚入门的C++,为了生计作起了现场服务、维护的工程师。

作了一年,心想自己总不能这样发展下去,这时大概因为工程作的不错吧,公司说应把最好的技术人员放到市场一线,现在想来是利用我更能欺骗用户,也给了贪官们作出选择的借口。

于是乎又有了开辟市场的雄心,便和中国特色的销售员一道投身与中国特色的市场中一作就是两年。

两年中市场的黑暗、艰辛使我雄心全无,终于知道那一片天地不属于自己,这个行业不适合研发,加之合同到期,终于决定动一动了。

因此马上毕业的诸君选择适合自己的行业与职务是很重要的。

既然决定动一动,便有了作程序员的想法,买了几本书籍啃了一个月,便到一家国内著名的通讯公司去面试了两次,结果不得而知,当然是失败。

好在本人没有放弃跳槽的努力,终于被另一家国内著名的通讯公司录用,可惜不是程序员的职位,而是作软件的测试而已。

笔者心目中的程序员大致应是诚信、刻苦,最重要的是应富于思考,这一点与碌碌之编程者是截然不同的,虽然两者同样需要为生计而打拼。

塞翁失马,焉知非福,或许既没有成为程序员,也没有成为编程者,也便有了更多的时间学习与思考,因为现今为了生计打拼只用发挥40%的精力即可,剩下的时间又没有在报纸与茶水中虚度,竟不想这一年多能学会和澄清了这许多东西(在校7年的荒废与高校误人由此可见一斑),尤其是一些思想层面的东西。

思想源于交流,也载于交流,心想一年多的学习钻研收获总该总结一下,也算给自己没有荒废时光的一个诠释。

现在高校的教材流于形式,大师们的专著博大精深,深入理解非一日之功,因此也想借这本小册子给初学者以启发。

身边碌碌编程者似乎也不少,现在的傻瓜工具造就了无数编程者,希望也对他们能有所警醒。

感谢C、C++、COM、OS、COM等创造者,是他们成就了编程思想。

感谢将这些思想著书的编程大师们,是他们传播了编程思想。

感谢我的妻子,是她喋喋不休的抱怨(一直催我进入编程者的行列)多少促使我写了这本小册子。

也要感谢领导安排给我的工作使我剩下诸多时间思考。

本书力图以简短的例子阐明一些思想,没有太多的旁征博引,只有朴素的思考。

思考使人奋进,但愿这本小册子能给你带去点什么。

实践是检验真理的唯一标准,真正的程序员不能仅靠思想,需要的是思想-编程-再思想-再编程,周而复始步步登顶,希望大家不要如我和碌碌编程者们。

书中或许有些许胡言乱语,定是写的疲惫时给自己提神而已,诸君切莫见怪。

一直惶恐这小册子当不起编程思想四个字,但这是笔者的理想,非常欢迎读者对本书提出批评和建议,抑或有伯乐想拯救我那快逝去的理想。

请mailto:

shichengzan@与我联系。

路漫漫其修远兮,吾将上下而求索。

非程序员

2001/11/17于深圳

目录

第一章高效无错代码1

1.1概要1

1.2编程策略2

1.2.1命名约定2

1.2.2使用断言4

1.2.3优化/效率5

1.2.4其他5

1.3模块化的C编程6

1.4OO方法与C++9

1.4.1C++运行效率分析9

1.4.2封装程度原则11

1.4.3静态成员13

1.4.4继承与组合14

1.4.5多重继承与虚基类14

1.4.6多态15

1.4.7虚析构函数17

1.4.8new、delete与指针19

1.4.9使用const20

1.5没有指针怎么办22

1.6从通用链表看代码复用26

1.6.1传统方法26

1.6.2多态方法(虚函数与模板)29

1.7C++对象复用32

1.7.1C++客户复用C++对象32

1.7.2将C++对象打包进DLL中34

1.7.3C++对象使用抽象基类35

附录:

这一年影响我的主要著作38

第一章高效无错代码

代码永远会有BUG,在这方面没有最好只有更好。

高效是程序员必须作到的事情,无错是程序员一生的追求。

复用、分而治之、折衷是代码哲学的基本思想。

模块化与面向对象是实现高效无错代码的方法。

高效无错代码需要思想与实践的不断反复。

1.1概要

软件生命期按一般的划分(线性模型)可以分为:

需求分析、设计、编程、调试、维护几个阶段。

 

图1.1软件生命期线性模型

事实上软件生命期比上述模型复杂的多,人们有提出了许多复杂模型,比如以下的渐增模型。

 

图1.2软件生命期渐增模型

软件生命期并不短,但也不会很长(这因素较多,往往由于市场原因使软件生命终结),在这个生命期中,各个阶段时间大致相同,重要程度不分伯仲。

如何保证软件的高效无错是很复杂的问题,笔者实在不敢阐述(交给软件工程的专家吧),但应该清楚的认识到,高效无错的代码是通过优秀的设计、编码得到,一个软件的失败不能总将原因推给需求分析的不准确,不能总将筹码压倒测试上,就如不能企盼医者让病入膏肓者重获健康,让奄奄一息者起死回生一样。

如何适应不断变化的市场需求,如何在设计出可复用的代码等等是程序员的责任与使命,从编程规范到代码设计都需要思想,而不要急于编码。

“代码英雄”的时代已一去不返,就算是为了生计而编程,那么良好的设计与编程方法,也可以提高效率,作到事半功倍。

本章将就编程策略、OO方法于C++、代码复用思想等谈一谈体会。

1.2编程策略

好的编程规范才能造就好的代码,编程规范不是对程序员的束缚,而是指引与明镜。

在现今人类虚伪远多于诚实,还好人类造就的CPU依然诚实(这只能感激造就他的大师们)。

CPU会永远无误地执行你的代码指令(除非有其他电磁干扰),如果结果未如你所愿,请相信一点:

Youarewrong,notCPU。

编程不比吟诗作画,不需要浪漫和神来之笔。

灵感应用于设计与思想,代码应是思想以规范形式的体现,不是让人看不懂方为高人。

我想编程策略可以写一本书,这里只重点谈几个问题。

1.2.1命名约定

命令规范基本上采用了微软推荐的匈牙利命名法,略有简化。

1.常量

常量由大写字母和数字组成,中间可以下划线分隔,如CPU_8051。

2.变量

变量由小写(变量类型)字母开头,中间以大写字母分隔,可以添加变量域前缀(变量活动域前缀以下划线分隔)。

如:

v_nAcVolMin(交流电压最小值)。

变量域前缀见下表

 

变量域

项目

备注

v_

variable

全局变量

i_

interrupt

中断函数中的全局变量

s_

Static

静态变量

m_

成员变量

局部变量不用

变量类型前缀见下表

变量类型

项目

备注

U

unsignedchar

c

Char

n

unsignedint

i

Int

f

float

d

Double

p

Point

复用时放在前面

l

Long

str

string

b

BOOL

t

struct

C

class

I

interface

接口

un

union

a

array

复用时放在前面

e

enum

变量类型前缀可以复用,如afPara[20]。

对于int,float,double型的局部变量,如果变量名的含义十分明显,则不加前缀,避免烦琐。

如用于循环的int型变量i,j,k;float型的三维坐标(x,y,z)等。

3.函数名一般由大写字母开头,中间以大写字母分隔,如SetSystemPara。

函数命名采用动宾形式。

如果函数为最底层,可以考虑用全部小写,单词间采用带下划线的形式。

如底层图形函数:

pixel、lineto以及读键盘函数get_key等。

4.符号名应该通用或者有具体含义,可读性强。

尤其是全局变量,静态变量含义必须清晰。

C++中的一些关键词不能作为符号名使用,如class、new、friend等。

符号名长度小于31个,与ANSIC保持一致。

命名只能用26个字母,10个数字,以及下划线‘_’来组成,不要使用‘$’‘@’等符号。

下划线‘_’使用应该醒目,不能出现在符号的头尾,只能出现在符号中间,且不要连续出现两个。

5.程序中少出现无意义的数字,常量尽量用宏替代。

1.2.2使用断言

程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用。

断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。

以下是一个内存复制程序,在运行过程中,如果assert的参数为假,那么程序就会中止(一般地还会出现提示对话,说明在什么地方引发了assert)。

//复制不重叠的内存块

voidmemcpy(void*pvTo,void*pvFrom,size_tsize)

{

void*pbTo=(byte*)pvTo;

void*pbFrom=(byte*)pvFrom;

assert(pvTo!

=NULL&&pvFrom!

=NULL);

while(size-->0)

*pbTo++=*pbFrom++;

return(pvTo);

}

assert不是一个仓促拼凑起来的宏,为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。

所以assert不是函数,而是宏。

程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。

以下是使用断言的几个原则:

1)使用断言捕捉不应该发生的非法情况。

不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。

2)使用断言对函数的参数进行确认。

3)在编写函数时,要进行反复的考查,并且自问:

“我打算做哪些假定?

”一旦确定了的假定,就要使用断言对假定进行检查。

4)一般教科书都鼓励程序员们进行防错性的程序设计,但要记住这种编程风格会隐瞒错误。

当进行防错性编程时,如果“不可能发生”的事情的确发生了,则要使用断言进行报警。

1.2.3优化/效率

规则一:

对于在中断函数/线程和外部函数中均使用的全局变量应用volatile定义。

例如:

volatileintticks;

voidtimer(void)interrupt1//中断处理函数

{

ticks++

}

voidwait(intinterval)

{

tick=0;

while(tick

}

如果未用volatile,由于while循环是一个空循环,编译器优化后(编译器并不知道此变量在中断中使用)将会把循环优化为空操作!

这就显然不对了。

规则二:

不要编写一条过分复杂的语句,紧凑的C++/C代码并不见到能得到高效率的机器代码,却会降低程序的可理解性,程序出错误的几率也会提高。

规则三:

变量类型编程中应用原则:

尽量采用小的类型(如果能够不用“Float”就尽量不要去用)以及无符号Unsigned类型,因为符号运算耗费时间较长;同时函数返回值也尽量采用Unsigned类型,由此带来另外一个好处:

避免不同类型数据比较运算带来的隐性错误。

1.2.4其他

规则一:

不要编写集多种功能于一身的函数,在函数的返回值中,不要将正常值和错误标志混在一起。

规则二:

不要将BOOL值TRUE和FALSE对应于1和0进行编程。

大多数编程语言将FALSE定义为0,任何非0值都是TRUE。

VisualC++将TRUE定义为1,而VisualBasic则将TRUE定义为-1。

例如:

BOOLflag;

if(flag){//dosomething}//正确的用法

if(flag==TRUE){//dosomething}//危险的用法

if(flag==1){//dosomething}//危险的用法

if(!

flag){//dosomething}//正确的用法

if(flag==FALSE){//dosomething}//不合理的用法

if(flag==0){//dosomething}//不合理的用法

规则三:

小心不要将“==”写成“=”,编译器不会自动发现这种错误。

规则四:

建议统一函数返回值为无符号整形,0代表无错误,其他代表错误类型。

1.3模块化的C编程

C语言虽然不具备C++的面向对象的成分,但仍应该吸收面向对象的思想,采用模块化编程思路。

面向对象的思想与面向对象的语言是两个概念。

非面向对象的语言依然可以完成面向对象的编程,想想C++的诞生吧!

C++没有理由对C存在傲慢与偏见,不是任何场合C++方法都是解决问题的良药,譬如面对嵌入式系统效率和空间的双重需求。

注意我们谈的是方法,而不是指编译器。

C在软件开发上存在的首要问题是缺乏对数据存取的控制(封装),C编程者乐而不疲的使用着大量extern形式的全局变量在各模块间交换着数据,“多方便啊”编程者乐曰,并传授给下一个编程者。

这样多个变量出现在多个模块中,剪不断理还乱,直到有一天终于发现找一个“人”好难。

一个东西好吃,智者浅尝之改进之,而愚者只会直至撑死。

这世上本没有什么救世主,应在C上多下功夫,程序员和C缔造者早就有过思考,相信野百合也有春天,还是看看C语言如何实现模块化编程方法,在部分程度上具备了OO特性封装与多态。

在具体阐述之前,需要明确生存期与可见性的概念。

生存期指的是变量在内存的生存周期,可见性指的是变量在当前位置是否可用。

两者有紧密联系,但不能混为一谈。

一个人存在但不可见只能解释成上帝或灵魂,一个变量存在但不可见却并非咄咄怪事,模块化方法正是利用了静态函数、静态变量这些“精灵”们特殊的生存期与可见性。

最后需要明确一点的是这里的模块是以一个.C文件为单位。

规则一:

利用函数命名规则和静态函数

模块中不被其他模块调用的内部函数采用以下命名规则:

用全部小写,单词间采用带下划线的形式。

如底层图形函数:

pixel、lineto以及读键盘函数get_key等。

这些函数应定义为static静态函数,这样在其他模块错误地调用这些函数时编译器能给出错误(如BC编译器)。

(注意:

有些编译器不能报告错误,但为了代码风格一致和函数层次清晰,仍建议这样作)。

规则二:

利用静态变量

模块中不能被其他模块读写的全局变量应采用static声明,这样在其他模块错误地读写这些变量时编译器能给出警告(C51编译器)或错误(BC编译器)。

规则三:

引入OO接口概念和指针传参

模块间的数据接口(也就是函数)应该事先较充分考虑,需要哪些接口,通过接口需要操作哪些数据,尽量作到接口的不变性。

模块间地数据交换尽量通过接口完成,方法是通过函数传参数,为了保证程序高效和减少堆栈空间,传大量参数(如结构)应采用传址的方式,通过指针作为函数参数或函数返回指针,尽量杜绝extern形式的全局变量,请注意是extern形式的全局变量,模块内部的全局变量是允许和必须的。

传指针参数增加的开销主要是作参数的指针和局部指针的数据空间(嵌入式系统(如C51)往往由于堆栈空间有限,函数参数会放到外部RAM的堆栈中),增加的代码开销仅是函数的调用,带来的是良好的模块化结构,而且使用接口函数会比在代码中多处直接使用全局变量大大节约代码空间。

需注意一点的事物总有他的两面性,水能载舟,也能覆舟。

对于需要频繁访问的变量如果仍采用接口传递,函数调用的开销是巨大的,这时应考虑仍采用extern全局变量。

以下演示了两个C模块交换数据:

//Module1.C

OneStruct*voidGetOneStruct(void);//获取模块1数据接口

voidSetOneStruct(OneStruct*pOneStruct);//写模块1数据接口

structOneStruct

{

intm_imember;

//……

}t1;//模块1的数据

//t1初始化代码…..

OneStruct*voidGetOneStruct(void)

{

OneStruct*pt1;//只需定义一个局部变量

t1.imember=15;

pt1=&t1;

returnpt1;

}

voidSetOneStruct(OneStruct*pOneStruct)

{

t1.imember=pOneStruct->imember;

//…….

}

//Module2.C

voidOperateOneStruct(void);//模块2通过模块1提供的接口操作模块1的数据

OneStruct*voidGetOneStruct(void);

voidSetOneStruct(OneStruct*pOneStruct);

voidOperateOneStruct(void)

{

OneStruct*pt2;//只需定义一个局部变量

pt2=GetOneStruct();//读取数据

SetOneStruct(pt2);//改写数据

}

采用接口访问数据可以避免一些错误,因为函数返回值只能作右值,全局变量则不然。

例如cOneChar==4;可能被误为cOneChar=4;

规则四:

有限的封装与多态

不要忘记C++的class源于C的struct,C++的虚函数机制实质是函数指针。

为了使数据、方法能够封装在一起,提高代码的重用度,如对于一些与硬件相关的数据结构,建议采用在数据结构中将访问该数据结构的函数定义为结构内部的函数指针。

这样当硬件变化,需要重写访问该硬件的函数,只要将重写的函数地址赋给该函数指针,高层代码由于使用的是函数指针,所以完全不用动,实现代码重用。

而且该函数指针可以通过传参数或全局变量的方式传给高层代码,比较方便。

例如:

structOneStruct

{

intm_imember;

int(*func)(int,int);

//……

}t2;

1.4OO方法与C++

时下程序员与编程者无一不在使用C++编译器,但并非每个人都在使用OO方法编程,新瓶装旧酒者大有人在,尤其在嵌入式系统领域。

是什么限制了C++方法的使用,古云:

士欲善其事必先利其器,现利器在手却如同钝刀,牛刀给只会杀鸡的妇人能杀牛吗?

我认为还是对OO方法与C++的理解存在一些问题。

你或许和我一样曾存在许多问题:

何时应使用友元、继承与组合有什么不同、何时应用虚函数,什么是静态成员……等等,这些问题让我们共同一一思考吧。

1.4.1C++运行效率分析

OO方法有三个基本概念:

“类与对象”、“继承与组合”、“虚函数与多态”。

理解这些概念,有助于提高程序的质量,特别是提高“可复用性”与“可扩充性”。

C++实现了OO方法的三个基本概念,与C比较在效率上有什么不同呢?

我们应该分清楚编译器期行为和运行期行为,只有运行期行为才会影响程序的速度,编译期行为对代码/数据的开销有影响。

另外时间与空间两个因素对于程序而言,永远是一对矛盾,我们需要作的是折衷,不是时间换空间,便是空间换时间,在此不由感叹中国悠久的中庸之道的光辉,却不明其何以未能为中国的现代文明带来辉煌。

分析一:

C++引入类与对象,类比于C的结构和函数原型列表,C++根据pubic、private关键字决定数据与方法访问的允许性,这些决定在编译期完成,因此单纯加入类即不会影响代码的执行速度,也不会影响代码的大小,仅仅影响了成员函数参数表的大小,C++会在成员函数参数表中加入一个this指针,这个开销是很小的。

分析二:

默认参数值也是基本没有运行损失的,编译器只是加入代码使得在每次函数被无参数调用时传递一个默认的值。

保持良好的调用习惯是必须的,默认参数值可以作为一种容错手段。

值得注意的是代码大小增加了,这点开销看你是否能接受,若不行可以不用默认参数值,这不是拒绝使用C++的理由。

分析三:

函数名重载也是在编译时的修改,具有相同名字但不同参数的函数在编译过程中分配了一个唯一的名字,由编译器编译连接时进行正确的匹配。

函数名重载对代码在时间空间上没有任何损失,只是增加了编译期的时间。

操作符重载与函数名重载基本相同。

分析四:

引用与传递指针是等效的,而且显得比较简明,这个特性也并不是必须的。

分析五:

构造函数和析构函数有一些相关的损失,其保证对象在创建和超出范围时被自动调用,增加了运行开销,然而这个小量的开销对于减少错误而言是合理的代价。

分析六:

继承机制的运行开销(不采用虚函数)基本没有,相反提供了良好的代码重用机制。

分析七:

C++真正运行开销较大的是虚函数和模板。

虚函数需要为每个继承链上的对象增加一个vtable表,模板需要在水平方向适应不同类型。

但两者能保持合理的性能代价比,实现代码的真正重用。

综上所述,C++三大特性中对运行特性影响较大的是多态性,记住世上永没有免费的午餐,是否采用C++的新特性,完全可以因地制宜。

对于PC系统,C++增加的开销完全是可以被带来的增益所抵消,关于这一点,在本书后续的章节中会以具体实现加以论述。

对于嵌入式系统,64K以上代码的系统是可以考虑采用类、继承特性,适当采用虚函数特性,对于模板则要慎重;64K代码以下的系统由于代码数据的资源均有限,可以采用上节提到的C模块化方法。

1.4.2封装程度原则

对象(Object)是类(Class)的一个实例(Instance)。

如果将对象比作房子,那么类就是房子的设计图纸。

所以面向对象程序设计的重点是类的设计,而不是对象的设计。

类可以将数据和函数封装在一起,其中函数表示了类的行为(或称服务)。

其基本原则是数据私有,函数共有,只有成员函数才能访问私有的数据。

类提供关键字public、protected和private用于声明哪些数据和函数是公有的、受保护的或者是私有的。

这样可以达到信息隐藏的目的,即让类仅仅公开必须要让外界知道的内容,而隐藏其它一切内容。

我们不可以滥用类的封装功能,封装不是万能的真主,只有成员函数才能访问私有数据在某些场合将成为效率的灾难。

例如:

classA

{

private:

intimember;

public:

intGeti(){r

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 卡通动漫

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1