欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    C++编程高质量培训.pdf

    • 资源ID:89823018       资源大小:11.39MB        全文页数:87页
    • 资源格式: PDF        下载积分:12金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要12金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    C++编程高质量培训.pdf

    vv Back to man.ChinaU高质量C+/C编程指南文件状态1 草稿文件 J 正式文件口更改正式文件文件标识:当前版本:1.0作者:林锐博士完成日期:2001年7月24日版 本 历 史版本/状态作者参与者起止日期备注V0.9草稿文件林锐2001-7-1S2001-7-18林锐起草V 1.0正式文件林锐2001-7-18S2001-7-24朱洪海审查V 0.9,林锐修正草稿中的错误目录前 言.第1章文件结构.1.1 版权和版本的声明1.2 头文件的结构.1.3 定义文件的结构.1.4 头文件的作用.1.5 目录结构第2章程序的版式2.1 空行2.2 代码行2.3 代码行内的空格2.4 对齐2.5 长行拆分2.6 修饰符的俅置2.7 注释.2.8 类的版式.第3章命名规则.3.1 共性规则3.2 简单的WINDOWS应用程序命名规则3.3 简单的U N IX应用程序命名规则第4章表达式和基本语句.4.1 运算符的优先级4.2 复合表达式4.3 IF 语句.4.4 循环语句的效率.4.5 FOR语句的循环控制变量4.6 SW ITCH语句4.7 GOTO语句第5章 常 量.5.1 为什么需要常量5.2 CONST与#DEF1NE的比较5.3 常量定义规则.5.4 类中的常量第6章函数设计.6.1 参数的规则6.2 返|可值的规则6.3 函数内部实现的规则6.4 其它建议6.5 使用断言6.6 引用与指针的比较第7章内存管理.7.1 内存分配方式.7.2 常见的内存错误及其对策7.3 指针与数组的对比7.4 指针参数是如何传递内存的?7.5 FREE和 DELETE把指针-怎么啦?.7.6 动态内存会被自动释放吗?.7.7 杜 绝“野指针”.7.8 有了 MALLOC/FREE 为什么还要 NEW/DELETE?.7.9 内存耗尽怎么办?7.10 MALLOC/FREE的使用要点7.11 NEW/DELETE 的使用要点.7.12 一些心得体会第8章C+函数的高级特性.8.1 函数重载的概念.8.2 成员函数的而载、覆盖与隐藏8.3 参数的缺省值.8.4 运算符重载8.5 函数内联8.6 一些心得体会.第9章类的构造函数、析构函数与赋值函数.9.1 构造函数与析构函数的起源.9.2 构造函数的初始化表9.3 构造和析构的次序9.4 示例:类 STRING的构造函数与析构函数9.5 不要轻视拷贝构造函数名赋值函数9.6 示例:类 STRING的拷贝构造函数与赋值函数.9.7 偷懒的办法处理拷贝构造函数与赋值函数9.8 如何在派生类中实现类的基本函数.9 9 些心得体会.第10章类的继承与组合.10.1 继承10.2 组合第11章其它编程经验.CONST提高函数的健壮性11.2 提高程序的效率II.3 一些有益的建议参考文献附录A:C+/C代码审查表附录B:C+/C试题.附录C:C+/C试题的答案与评分标准.二、本书导读首先请做附录B的C+/C试题(不要看答案),考查自己的编程质量究竟如何。然后参照答案严格打分。(1)如果你只得了几十分,请不要声张,也不要太难过。编程质量差往往是由于不良习惯造成的,与人的智力、能力没有多大关系,还是有药可救的。成绩越差,可以进步的空间就越大,中国不就是在落后中赶超发达资本主义国家吗?只要你能下决心改掉不良的编程习惯,第二次考试就能及格了。(2)如果你考及格了,表明你的技术基础不错,希望你能虚心学习、不断进步。如果你还没有找到合适的工作单位,不妨到上海贝尔试一试。(3)如果你考出8 5分以上的好成绩,你有义务和资格为你所在的团队作“C+/C编程”培训I。希望你能和我们多多交流、相互促进。半年前我曾经发现一颗好苗子,就把他挖到我们小组来。(4)如果你在没有任何提示的情况下考了满分,希望你能收我做你的徒弟。编程考试结束后,请阅读本书的正文。本书第一章至第六章主要论述C+/C编程风格。难度不高,但是细节比较多。别小看了,提高质量就是要从这些点点滴滴做起。世上不存在最好的编程风格,一切因需求而定。团队开发讲究风格一致,如果制定了大家认可的编程风格,那么所有组员都要遵守。如果读者觉得本书的编程风格比较合你的工作,那么就采用它,不要只看不做。人在小时候说话发音不准,写字潦草,如果不改正,总有后悔的时候。编程也是同样道理。第七章至第十一章是专题论述,技术难度比较高,看书时要积极思考。特别是第七章“内存管理”,读了并不表示懂了,懂了并不表示就能正确使用。有一位同事看了第七章后觉得“野指针”写得不错,与我切磋了一把。可是过了两周,他告诉我,他忙了两天追查出一个B u g,想不到又是“野指针”出问题,只好重读第七章。光看本书对提高编程质量是有限的,建议大家阅读本书的参考文献,那些都是经典名著。如果你的编程质量已经过关了,不要就此满足。如果你想成为优秀的软件开发人员,建议你阅读并按照CMMI规范做事,让自己的综合水平上升一个台阶。上海贝尔的员工可以向网络应用事业部软件工程研究小组索取CMMI有关资料,最好能参加培训。三、版权声明本书的大部分内容取材于作者一年前的书籍手稿(尚未出版),现整理汇编成为上海贝尔网络应用事业部的一个规范化文件,同时作为培训教材。由于C+/C编程是众所周知的技术,没有秘密可言。编程的好经验应该大家共享,我们自己也是这么学来的。作者愿意公开本书的电子文档。版权声明如下:(1)读者可以任意拷贝、修改本书的内容,但不可以篡改作者及所属单位。(2)未经作者许可,不得出版或大量印发本书。(3)如果竞争对手公司的员工得到本书,请勿公开使用,以免发生纠纷。预计到2002年7月,我们将建立切合中国国情的CMMI 3级解决方案。届时,包括本书在内的约1000页规范将严格受控。欢迎读者对本书提出批评建议。林锐,2001年7月第1章文件结构每 个 C+/C程序通常分为两个文件。一个文件用于保存程序的声明(declaration),称为头文件。另一个文件用于保存程序的实现(implementation),称为定义(definition)文件。C+/C程序的头文件以“.h”为后缀,C 程序的定义文件以“.c”为后缀,C+程序的定义文件通常以“.cpp”为后缀(也有一些系统以“.cc”或“.cxx”为后缀)。L1版权和版本的声明版权和版本的声明位于头文件和定义文件的开头(参见示例i-i),主要内容有:(1)版权信息。(2)文件名称,标识符,摘要。(3)当前版本号,作者/修改者,完成日期。(4)版本历史信息。/*Copyright(c)2001,上海贝尔有限公司网络应用事业部*All rights reserved.*文件名称:filename,h*文件标识:见配置管理计划书*摘 要:简要描述本文件的内容*当前版本:1.1*作 者:输入作者(或修改者)名字*完成H期:2001 年7 月20日*取代版本:1.()*原 作 者:输入原作者(或修改者)名字*完成日期:2001 年5 月1 0日*/示例1 T 版权和版本的声明工 工 头文件的结构头文件由三部分内容组成:(1)头文件开头处的版权和版本声明(参见示例1-1)。(2)预处理块。(3)函数和类结构声明等。假设头文件名称为graphics,h,头文件的结构参见示例1-2。【规则1-2-1】为了防止头文件被重复引用,应当用ifndeiVdefine/endif结构产生预处理块。【规贝打-2-2】用#in clu d e filenamej 格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。【规则1-2-3】m#include“filename.h”格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。令 【建议1 2 1】头文件中只存放“声明”而不存放“定义”在 C+语法中,类的成员函数可以在声明的同时被定义,并且自动成为内联函数。这虽然会带来书写上的方便,但却造成了风格不一致,弊大于利。建议将成员函数的定义与声明分开,不论该函数体有多么小。令【建议1-2-2 不提倡使用全局变量,尽量不要在头文件中出现象extern int value这类声明。版权和版本声明见示例1-1,此处省略。#ifndef GRAPHICS_H 防止 graphics.h 被重复引用tfdefine GRAPHICS_HSinclude 引用标准库的头文件#includemyheader.h 引用非标准库的头文件void Functionl();/全局函数声明class Box 类结构声明ttendif示例1-2 C+/C头文件的结构1宜定义文件的结构定义文件有三部分内容:(1)定义文件开头处的版权和版本声明(参见示例1-1)。(2)对一些头文件的引用。(3)程序的实现体(包括数据和代码)。假设定义文件的名称为graphics,c p p,定义文件的结构参见示例1-3。版权和版本声明见示例1-1,此处省略。#include“graphics.h 引用头文件/全局函数的实现体void Functionl(,)()/类成员函数的实现体void Box:Draw()()示例1-3 C+/C定义文件的结构工 盘 头文件的作用早期的编程语言如Basic、Fortran没有头文件的概念,C+/C语言的初学者虽然会用使用头文件,但常常不明其理。这里对头文件的作用略作解释:(1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。(2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。L5目录结构如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别保存于不同的目录,以便于维护。例如可将头文件保存于include目录,将定义文件保存于source目 录(可以是多级目录)。如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声明”。为了加强信息隐藏,这些私有的头文件可以和定义文件存放于同一个目录。蜜 章程序的版式版式虽然不会影响程序的功能,但会影响可读性。程序的版式追求清晰、美观,是程序风格的重要构成因素。可以把程序的版式比喻为“书法”。好 的“书法”可让人对程序一目了然,看得兴致勃勃。差的程序“书法”如螃蟹爬行,让人看得索然无味,更令维护者烦恼有加。请程序员们学习程序的“书法”,弥补大学计算机教育的漏洞,实在很有必要。21空行空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。【规则2-1-1在每个类声明之后、每个函数定义结束之后都要加空行。参见示例2-1(a)【规则2-1-2在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。参见示例21(b)示例2-1(a)函数之间的空行 示例2T(b)函数内部的空行/空行/空行void Functionl()w hile(co n d itio n)(.sta te m e n t1;)/空行/空行i f (co n d itio n)void Function2()(sta te m e n t2;)e ls e/空行void Functions(,)sta te m e n ts;()/空行sta te m e n t4;)建 代码行【规则2-2-1】一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。【规则2-2-2】if、for、while,d o 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加 。这样可以防止书写失误。示例2-2(a)为风格良好的代码行,示例2-2(b)为风格不良的代码行。i n t w i dt h;/宽度i n t he i ght;/高度i n t de p t h;/深度i n t w i dt h,he i ght,de p t h;/宽度高度深度x =a+b;y =c+d;z =e +f;X=a+b;y =c+d;z =e +f;i f(w i dt h he i ght)(do s o m e t hi n g();)i f(w i dt h virtual、inline、case等关键字之后至少要留一个空格,否则无法辨析关键字。象if、for、while等关键字之后应留一个空格再跟左括 号(,以突出关键字。【规则2-3-2】函数名之后不要留空格,紧跟左括号(,以与关键字区别。【规则2-3-31向后紧跟,D、,、向前紧跟,紧跟处不留空格。【规则2-3-4*,之后要留空格,如Functions,y,z)。如 果 不 是 一 行 的 结 束 符 号,其后要留空格,如 for(initialization;condition;update)【规则2 3 5 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+=”=、“=、+”、“*”、“”、“&”、“|、等二元操作符的前后应当加空格。【规则2 3 6】-元操作符如“!”、“+”、“一”、“&”(地址运算符)等前后不加空格。【规贝112-3-7】象“口”、这类操作符前后不加空格。建议2-3-1】对于表达式比较长的fo r 语句和i f语句,为了紧凑起见可以适当地去掉一些空格,如 fo r (i=0;i 1 0;i+)和 i f(a=2 0 0 0)良好的风格i f(y e ar 2 0 0 0)/不良的风格i f(a=b)&(c=b&c=d)不良的风格fo r (i=0;i 1 0;i+)/良好的风格fo r(i=0;i 1 0;i+)/不良的风格fo r (i =0;I 1 0;i +)过多的空格x =a b?a:b;/良好的风格x=a Fu n ct i o n ();/不要写成 b-Fu n ct i o n();示例2-3 代码行内的空格刿对齐【规则2-4-1】程序的分界符 和 应独占一行并且位于同一列,同时与引用它们的语句左对齐。【规则2-4-2 之内的代码块在 右边数格处左对齐。示例2 4 (a)为风格良好的对齐,示例2-4 (b)为风格不良的对齐。void Function(int x)void Function(int x)/program code/program code)if(condition)if(condition)/program code/program code)else()else /program code)示例2-4(a)风格良好的对齐 示例2-4(b)风格不良的对齐/program code)for(initialization;condition;update)(/program code)for(initialization;condition;update)/program code)While(condition)(/program code)while(condition)/program code如果出现嵌套的,则使用缩进对齐,如:(会 长 行拆分【规则251】代码行最大长度宜控制在70 至8 0 个字符以内。代码行不要过长,否则眼睛看不过来,也不便于打印。【规则2 5-2长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。if (ve ry_ longe r_ va ria b 1 e 1 =ve ry_ longe r_ va ria b le l2)&(ve ry_ longe r_ va ria b le 3 =ve ry_ longe r_ va ria b le 1 4)&(ve ry_ longe r va ria b le s DrawO;类的成员函数【规则3-1-8 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。例如:int minValue;int maxValue;int SetValue(.);int GetValue(.);。【建议3-1-1】尽量避免名字中出现数字编号,如 Valuel,Value2等,除非逻辑上的确需要编号。这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字(因为用数字编号最省事)。3.2简单的Windows应用程序命名规则作者对“匈牙利”命名规则做了合理的简化,下述的命名规则简单易用,比较适合于Windows应用软件的开发。【规则3-2-1】类名和函数名用大写字母开头的单词组合而成。例如:class Node;/类名class LeafNode;/类名void Draw(void);/函数名void SetValue(int value);/函数名【规则322】变量和参数用小写字母开头的单词组合而成。例如:BOOL flag;int drawMode;【规则3-2-3】常量全用大写的字母,用下划线分割单词。例如:const int MAX=100;const int MAX LENGTH=100;【规则3-2-4静态变量加前缀$_ (表示static)。例如:void Init(.)(static int s_i nit Value;静态变量)【规则3-2-5如果不得已需要全局变量,则使全局变量加前缀(表示global)。例如:int g_howManyPeople;/全局变量int g_howMuchMoney;全局变量【规则3 2 6】类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名。例如:void Object:SetValue(int width,int height)(m_width=width;m_height=height:【规则3-2-7为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为各种标识符加上能反映软件性质的前缀。例如三维图形标准OpenGL的所有库函数均以g l开头,所有常量(或宏定义)均以GL开头。3殳简单的Unix应用程序命名规则第4章表达式和基本语句读者可能怀疑:连i f、f o r、w h i l e、g o t o、s w i t ch这样简单的东西也要探讨编程风格,是不是小题大做?我真的发觉很多程序员用隐含错误的方式写表达式和基本语句,我自己也犯过类似的错误。表达式和语句都属于C+/C的短语结构语法。它们看似简单,但使用时隐患比较多。本章归纳了正确使用表达式和语句的一些规则与建议。生1运算符的优先级C+/C语言的运算符有数十个,运算符的优先级与结合律如表4-1所示。注意一元运算符+-*的优先级高于对应的二元运算符。表4-1运算符的优先级与结合律优先级运算符结合律()-.从左至右从!+.(类 型)s i z eo f+-*&从右至左*/%从左至右IWJ+-从左至右 从左至右到 =从左至右=!=从左至右低&从左至右排A从左至右1从左至右列&从左至右II从右至左?:从右至左=+=-=*=/=%=&=A=1=从左至右【规则4-1-1】如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。由于将表4-1熟记是比较困难的,为了防止产生歧义并提高可读性,应当用括号确定表达式的操作顺序。例如:w o r d=(h i g h 8)I l o wi f (a l b)&(a&c)”复合表达式如a=b =c=()这样的表达式称为复合表达式。允许复合表达式存在的理由是:(I)书写简洁;(2)可以提高编译效率。但要防止滥用复合表达式。【规则4-2-1】不要编写太复杂的复合表达式。例如:i=a=b&c d&c +f=g +h ;/复合表达式过于复杂【规则4-2-2】不要有多用途的复合表达式。例如:d=(a =b +c)+r ;该表达式既求a值又求d值。应该拆分为两个独立的语句:a =b +c;d=a +r;【规则4-2-3】不要把程序中的复合表达式与“真正的数学表达式”混淆。例如:i f (a b c)/a b c 是数学表达式而不是程序表达式并不表示i f (a b)&(b c)而是成了令人费解的i f (a b)=或“=-E PS I N 0 N)&(x=E PS I N 0 N)其中E PS I N ON 是允许的误差(即精度)。4.3.4 指针变量与零值比较【规则4 3 4】应当将指针变量用“=或.=与 N U L L 比较。指针变量的零值是“空”(记为N U L L)。砥 N U L L 的值与0 相同,但是两者意义不同。假设指针变量的名字为P,它与零值比较的标准if语句如下:if(p =N U L L)/p与 N U L L 显式比较,强调p是指针变量if(p !=N U L L)不要写成if(P=0)/容易让人误解p是整型变量if(p !=0)或者if(P)/容易让人误解P是布尔变量if(!p)4.3.5 对 i f 语句的补充说明有时候我们可能会看到if(N U L L =p)这样古怪的格式。不是程序写错了,是程序员为了防止将if(p =N U L L)误 写 成 if(p =N U L L),而有意把p 和 N U L L 颠倒。编译器认为if(p =N U L L)是合法的,但是会指出if(N U L L =p)是错误的,因为N U L L 不能被赋值。程序中有时会遇到if/els e/r et ur n 的组合,应该将如下不良风格的程序if(c o n dit io n)r et ur n x;r et ur n y;改写为if(c o n dit io n)(r et ur n x;)els e(r et ur n y;或者改写成更加简练的return(condition?x:y);必 循 环语句的效率C+/C循环语句中,fo r语句使用频率最高,w hile语句其次,do语句很少用。本节重点论述循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。【建议4-4-1】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。例如示例4-4(b)的效率比示例4-4(a)的高。示例4-4(a)低效率:长循环在最外层 示例4-4(b)高效率:氏循环在最内层fbr(row=0;row100;row+)for(col=0;col5;col+)Ifor(col=0;col5;col+4-)Ifor(row=0;row100;row+)sum=sum+arowcol;tsum=sum+arowcol;)【建议4-4-2如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。示例4-4(c)的程序比示例4-4(d)多执行了 N T次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如 果 N 非常大,最好采用示例4-4(d)的写法,可以提高效率。如 果 N 非常小,两者效率差别并不明显,采用示例4-4(c)的写法比较好,因为程序更加简洁。表4-4(c)效率低但程序简洁表4-4(d)效率高但程序不简洁for(i=0;iN;i+)if(condition)(if (condition)for(i=0;iN;i+)DoSomethingO;DoSomething();elseDoOtherthing();else)(for(i=0;iN;i+)DoOtherthing();)4.5 for语句的循环控制变量【规则4-5-1不可在for循环体内修改循环变量,防止for循环失去控制。【建议4-5-1】建议f o r 语句的循环控制变量的取值采用“半开半闭区间”写法。示例4-5(a)中的x 值属于半开半闭区间 0 =x N,起点到终点的间隔为N,循环次数为N。示例4-5(b)中的x值属于闭区间 0 =x =N-1 ,起点到终点的间隔为N-1,循环次数为N。相比之下,示例4-5 (a)的写法更加直观,尽管两者的功能是相同的。示例4-5(a)循环变量属于半开半闭区间示例4-5(b)循环变量属于闭区间f o r (i n t x=0;x N;x+)(f o r (i n t x=0;x 0 )*pbT o+=*pbF rom+;retu rn pv T o;示例6-5复制不重叠的内存块assert不是一个仓促拼凑起来的宏。为了不在程序的D ebu g版本和R elease版本引起差别,assert不应该产生任何副作用。所 以 assert不是函数,而是宏。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。如果程序在assert处终止了,并不是说含有该a sse rt的函数有错误,而是调用者出了差错,a sse rt可以帮助我们找到发生错误的原因。很少有比跟踪到程序的断言,却不知道该断言的作用更让人沮丧的事了。你化了很多时间,不是为了排除错误,而只是为了弄清楚这个错误到底是什么。有的时候,程序员偶尔还会设计出有错误的断言。所以如果搞不清楚断言检查的是什么,就很难判断错误是出现在程序中,还是出现在断言中。幸运的是这个问题很好解决,只要加上清晰的注释即可。这本是显而易见的事情,可是很少有程序员这样做。这好比一个人在森林里,看到树上钉着一块“危险”的大牌子。但危险到底是什么?树要倒?有废井?有野兽?除非告诉人们“危险”是什么,否则这个警告牌难以起到积极有效的作用。难以理解的断言常常被程序员忽略,甚至被删除。M a g u i r e,p 8-p 3 0【规则6-5-1使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。【规则6-5-2在函数的入口处,使用断言检查参数的有效性(合法性).【建议6-5-1在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定了的假定,就要使用断言对假定进行检查。【建议6-5-2一般教科书都鼓励程序员们进行防错设计,但要记住这种编程风格可能会隐瞒错误。当进行防错设计时,如 果“不可能发生”的事情的确发生了,则要使用断言进行报警。”引用与指针的比较引用是C+中的概念,初学者容易把引用和指针混淆一起。一下程序中,n是 m的一个引用(r e f e r e n c e),m 是被引用物(r e f e r e n t)。i n t m;i n t&n =m;n相当于m的 别 名(绰号),对 n的任何操作就是对m的操作。例如有人名叫王小毛,他的绰号是“三毛”。说“三毛”怎么怎么的,其实就是对王小毛说三道四。所 以 n既 不 是 m的拷贝,也不是指向m的指针,其实n就是用它自己。引用的一些规则如下:(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。(2)不能有N U L L 引用,引用必须与合法的存储单元关联(指针则可以是N U L L)。(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。以下示例程序中,k 被初始化为i 的引用。语句k =J 并不能将k 修改成为j的引用,只是把k的值改变成为6。由于k是 i 的引用,所 以 i 的值也变成了6。i n t i =5;i n t j =6;i n t&k =i;k =j;k 和 i 的值都变成了6;上面的程序看起来象在玩文字游戏,没有体现出引用的价值。引用的主要功能是传递函数的参数和返回值。C+语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。以下是“值传递”的示例程序.由于Fu n c l 函数体内的x是外部变量n的一份拷贝,改变x的值不会影响n,所以n的值仍然是0。v o i d Fu n c l(i n t x)x=x+10;int n=0;Fund(n);cout n=n endl;/n=0以下是“指针传递”的示例程序。由于Func2函数体内的x 是指向外部变量n 的指针,改变该指针的内容将导致n 的值改变,所以n 的值成为10。void Func2(int*x)(*x)=(*x)+10;int n=0;Func2(&n);cout n=n endl;/n=10以下是“引用传递”的示例程序。由于Func3函数体内的x 是外部变量n 的引用,x 和 n 是同一个东西,改变x 等于改变n,所以n 的值成为10。void Func3(int&x)(x=x+10;)int n=0;Func3(n);cout n=n endl;/n-10对比上述三个示例程序,会 发 现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?答案是“用适当的工具做恰如其分的工作”。指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?如果的确只需要借用一下某个对象的“别 名,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。第2章内存管理欢迎进入内存这片雷区。伟大的B i l l Ga t e s 曾经失言:6 4 0 K o u g h t t o b e e n o u g h f o r e v e r y b o d y一 B i l l Ga t e s 1 9 8 1程序员们经常编写内存管理程序,往往提心吊胆。如果不想触雷,唯一的解决办法就是发现所有潜伏的地雷并且排除它们,躲是躲不了的。本章的内容比一般教科书的要深入得多,读者需细心阅读,做到真正地通晓内存管理。ZJ内存分配方式内存分配方式有三种:(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,sta tic 变量。(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。(3)从堆上分配,亦称动态内存分配。程序在运行的时候用m a l l oc 或 n e w 申请任意多少的内存,程序员自己负责在何时用f re e 或 d e l e te 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。z2常见的内存错误及其对策发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。常见的内存错误及其对策如下:内存分配未成功,却使用了它。编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为N U L L。如 果 指 针 p 是函数的参数,那么在函数的入口处用a sse rt(p!=N U L L)进行检查。如果是用m a l l oc 或 n e w 来申请内存,应 该 用 if(p=N U L L)或if(p!=N U L L)进行防错处理。内存分配虽然成功,但是尚未初始化就引用它。犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。内 存分配成功并且已经初始化,但操作越过了内存的边界。例如在使用数组时经常发生下标“多1”或 者“少1”的操作。特别是在f o r 循环语句中,循环次数很容易搞错,导致数组操作越界。忘 记了释放内存,造成内存泄露。含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。动态内存的申请与释放必须配对,程序中m a l l o c与f r e e的使用次数一定要相同,否则肯定有错误(n e w/d e l e t e同理)。释 放了内存却继续使用它。有三种情况:(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。(2)函数的r e t u r n语句写错了,注意不要返回指向“栈内存”的“指针”或 者“引用”,因为该内存在函数体结束时被自动销毁。(3)使用f r e e或d e l e t e释放了内存后,没有将指针设置为NU LL。导致产生 野指针”。【规则7-2-1】用m a l l o c或n e w申请内存之后,应该立即检查指针值是否为N UL L。防止使用指针值为N U L L的内存。【规则7-2-2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。【规则7-2-3】避免数组或指针的下标越界,特别要当心发生“多1”或 者“少1”操作。【规则7-2-4动态内存的申请与释放必须配对,防止内存泄漏。【规则7 2 5】用f r e e或d e l e t e释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。Z3指针与数组的对比C+/C程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。下面以字符串为例比较指针与数组的特性。7.3.1修改内容示例7-3-1中,字符数组a的容量是6个字符,其内容为h e l l o 0 a的内容可以改变,如a 0 =指 针p指向常量字符串“w o r l d”(位于静态存储区,内容为w o r l d 0),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句p 0 =X,有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。c h a r a =h e l l o”;a 0 =X;c o u t a e n d l;c h a r *p w o r l d”;/注意p指向常量字符串p 0 /编译器不能发现该错误cout p endl;示例7-3-1修改数组和指针的内容7.3.2 内容复制与比较不能对数组名进行直接复制与比较。示例7-3-2中,若想把数组a的内容复制给数组b,不能用 语 句b=a,否则将产生编译错误。应该用标准库函数strcpy进行复制。同理,比 较b和a的内容是否相同,不能用if(b=a)来判断,应该用标准库函数strcmp进行比较。语句P=a并不能把a的内容复制指针p,而是把a的地址赋给了 p。要想复制a的内容,可以先用库函数malloc为p申请一块容量为strlen(a)+l个字符的内存,再用strcpy进行字符串复制。同理,语 句if(p=a)比较的不是内容而是地址,应该用库函数strcmp来比较。/数组char a口 =hello”;char b10;strcpy(b,a);/不能用 b=a;if(strcmp(b,a)=0)/不能用 if(b=a)/指针int len=strlen(a);char*p=(char*)malloc(sizeof(char)*(len+1);strcpy(p,a);/不要用 p=a;if(strcmp(p,a)=0)/不要用 if(p=a)示例7-3-2数组和指针的内容复制与比较7.3.3 计算内存容量用运算符sizeof可以计算出数组的容量(字节数)。示例7-3-3(a)中,sizeof(a)的值是12(注意别忘了0)。指针P指向a,但 是sizeof(p)的值却是4。这是因为s

    注意事项

    本文(C++编程高质量培训.pdf)为本站会员(奔***)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开