




已阅读5页,还剩167页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
改善程序设计技术的50个有效做法第二版2002.3ScottMeyers侯捷译,如何完成较好的设计如何避免常见的问题如何提高效率的一些准则不是放之四海而皆准的唯一真理,C+新标准新的类型bool,有两个值true,false.typedefintbool;constboolfalse=0;constbooltrue=1;,新的转型动作,static_cast(expression)/将表达式expression转为type类型const_cast(expression)/将常数类型expression转为非常数类型dynamic_cast(expression)/安全向下转型见39reinterpret_cast(expression)/函数指针类型转换不常用,1.尽量以const和inline取代#define,#define是一个宏,只能被预处理,而不被编译,用它定义的常量甚至不被编译器看见,因此不能发现使用中的错误。用#define定义一个简单函数,必须为每一个参数加上一个括号,容易造成错误。用内联函数高效准确。,defineratio1.653/编译器看不见ratio,只看见1.653/一旦出错,不会报告,constdoubleratio=1.653;constchar*constname=“ScottMeyers”;/字符串常量,InClass常量,用静态变量类内声明,类外定义。classEngineerConstantsprivate:staticconstdoubleFactor;constdoubleEngineerConstants:Factor=1.35;,2.尽量以取代,scanfprintf函数不能扩充用来输入输出自定义类型的变量。cinix;coutix;可以扩展,方便得多,改变旧有的C习惯(shiftingfromCtoC+)尽量以const和inline取代#define#define是一个宏,只能被预处理,而不被编译,用它定义的常量甚至不被编译器看见,因此不能发现使用中的错误。用#define定义一个简单函数,必须为每一个参数加上一个括号,容易造成错误。用内联函数高效准确。,3.尽量以new和delete取代malloc和free,malloc和free不能调用构造函数,析构函数new和delete则可。不能混用newdeletemallocfree必要用C库函数时检查是否用到malloc重新用new和delete改过。,4.尽量使用C+风格的注释形式,/*/要保证成对出现,不小心错一大片。/好看好读可以混合使用当心!definelight_speed3e8/m/sec(inavacum),内存管理(memorymanagement),new隐式调用构造函数,delete隐式调用析构函数,可以重载operatornew和operatordelete.不小心运用new和delete会导致各种错误。,5.使用相同形式的new和delete,string*a=newstring10;deletea;/出错deletea;/正确string*b=newstring;deleteb;/出错deleteb;/正确,typedefstringaddresslines4;,string*a=newaddresslines;deletea;/出错deletea;/正确不要对数组类型用typedef,不容易记住用哪一种delete,6.记得在析构函数中以delete对付指针成员,如果类中有指针数据成员,在每个构造函数中为指针成员配置内存,否则将它初始化为0(NULL指针)。若构造函数中用new配置了内存,一定要在析构函数中用delete释放在赋值运算重载时要将原有指针的内存删除,重新分配内存。要在析构函数中删除这个指针。,不要用delete删除一个未完成初始化的指针,不要删除一个未分配内存的指针。不要delete从一个类外面传来的指针。,7.为内存不足的状况预作准备,不能认为“检查内存是否分配成功”是多此一举。否则会出现严重后果。必须建立一个错误处理策略。当operatornew无法满足需求时,在抛出异常之前,会调用一个内存不足处理函数newhandler,这个函数由头文件提供。,typedefvoid(*new_handle)();,new_handlerset_new_handler(new_handlerp)throw();new_handler是一个函数指针,无参,无返回值。函数set_new_handler用来配置new_handler,参数和返回值都是函数指针,new_handler类型。它确定新的new_handler函数(参数),保留旧的new_handler函数(返回)。,可以自定义新的new_handler函数,用set_new_handler确认。,voidnomoreMemory()cerr成为类的成员函数,必要时作友元。要让函数式左边对象做类型转换,就不能做成员函数。,例子classcomplexcomplexoperator*(complexrhs)const;private:floatx,y;complexa(1,2),b(1.5,4);a=a*b;/正确a=a*2;/可以a=2*a;/出错只能声明为非成员函数constcomplexoperator*(constcomplex,20避免将数据成员设置为公有数据,让公有成员都是函数,可以保持一致性。将数据成员声明为私有成员或保护成员,可以确保数据的安全。,21尽可能使用const,使用const可以让编译器知道某值不能改变,编译器会确保这个条件不会被改变。constchar*p;/指针,指向常值字符串char*constp;/常指针,指向固定地址,地址内字符串不一定是常量constchar*constp;/常指针,指向固定地址,内置常字符串constchr*p;charconst*p;/意义相同,函数中const可以修饰传回值,参数,成员函数时甚至可以修饰整个函数。,函数返回值用const,可以改善函数的安全性,和效率。Ta2=b;/正确,constT/正确a2=b;/错误,constcomplexoperator*(constcomplex/不允许,参数用const可以保证参数值不变,让编译器作检查。const成员函数保证this指针不变。classApublic:intlength()const;private:intsize;;intA:length()constif(sizec?temp:c;inttemp=tempd?temp:dinttemp=tempe?temp:e;,使用max函数对两个参数,三个参数,直至五个参数都有效。但是,计算平均数就找不到合适的默认值,只好重载。一般,构造函数和拷贝构造函数的算法不同,需要重载。,25避免对指针类型和数值类型进行重载,voidf(intx);voidf(string*ps);f(0);/调用那一个?调用f(int)void*constNULL=0;/无类型指针f(NULL);/错误类型不符#defineNULL0f(NULL);/调用f(int)#defineNULL(void*)0)f(NULL);/错误类型不符,classNULLClass/类型名可以隐去public:templateoperatorT*()return0;/为任意类型T传回一个NULL指针NULL;f(string*ps);f(NULL);/NULL被转换为string*调用f(string*ps)尽可能避免对指针类型和数值类型进行重载,26防备隐性二义性状态,classB;classApublic:A(constB/错误模棱两可两种方法哪种更好?,voidf(int);voidf(char);doubled=6.02;f(d);/模棱两可模棱两可可以潜伏很久,直到爆发。,多继承最容易引发模棱两可。classBpublic:Bdoit();classCpublic:Cdoit();/放在私有成员中同样不行;classDerived:publicB,publicC;Derivedd;d.doit();/模棱两可d.B:doit();/正确d.C;doit();/正确,27如果不想使用编译器暗自产生的成员函数,明确地拒绝,不允许一个函数存在,只要不把它放进class中。但赋值函数,拷贝构造函数例外,系统会自行产生一个这种函数。不许对象调用某个函数,把它放在私有成员中。但公有函数,友元可以调用。声明一个函数,而不定义它,调用它编译器会指出错误。,28尝试切割globalnamespace(全局命名空间),标识符重名会引起混乱。同类名词冠以同一词头会使名字太长。建议使用namespace名字空间namespacesdmconstintBOOK_VERSION=2.0;classHandle;HandlegetHandle();,有三种方法取用namespace内的名字。,voidf1()usingnamespacesdm;/汇入所有名字coutBOOK_VERSION;Handleh=getHandle();,voidf2()usingsdm:BOOK_VERSION;/汇入单个名字coutBOOK_VERSION;/正确Handleh=getHandle();/错误voidf3()coutcreditInterest();,可以改为:for(list:iteratorp=allAccount.begin();p!=allAccount.end();+p)static_cast(*p)-creditInterest();/强制转型,向下转型!从基类向下到派生类的转型downcast会导致维护上的梦魇。/增加支票账户类classCheckingAccount:publicBankAccountpublic:voidCreditInterest();/将利息存入账户;,for(list:iteratorp=allAccount.begin();p!=allAccount.end();+p)if(*ppointstoaSavingAccount)static_cast(*p)-creditInterest();/强制向下转型!if(*ppointstoacheckingAcount)static_cast(*p)-creditInterest();/强制向下转型这是不符合C+精神的做法。C+中以对象类型决定不同行为,应该使用虚函数。,classBankAccount;/如前classinterestBearingAccount:publicBankAccountpublic:virtualvoidcreditInterest()=0;;classSavingAccount:publicInteresttBearingAccount;/如前classcheckingAccount:publicInteresttBearingAccount;/如前listallIBAccount;/银行中所有“须付利息的账户”,for(list:iteratorp=allIBAccount.begin();p!=allIBAccount.end();+p)(*p)-creditInterest();/正确,也适用于将来downcast向下转型,可以用虚函数的方法消除。向下转型容易出错,难以理解,难以维护。如果非向下转型不可,可以采用“安全向下转型动作”(safedowncasting),dynamic_cast将一个指针指向的动态对象转变成要求的类型,如果失败,传回null指针。classBankAccount;/如前classSavingAccount:publicBAnkAccount;/如前classcheckingAccount:publicBankAccount;/如前listallAccount;/银行中所有账户voiderror(conststring,for(list:iteratorp=allAccount.begin();p!=allAccount.end();+p)if(SavingAccount*psa=dynamic_cast(*p)psa-creditInterest();/安全转型,向下转型!elseif(checkingAcount*pca=dynamic_cast(*p)pca-creditInterest();/安全向下转型!elseerror(“unknownaccounttype!”);,向下转型失败时可以侦察得到。但是,如果有人新加入一个账户类型,又忘记更新以上代码,上面这个程序中所有downcast都会失败。downcast会导致程序难以维护。比起虚函数相差太远。非万不得已,不要出此下策。,40通过layering技术来模塑has-a或is-implemented-in-terms-of的关系,layering技术:用另一个类的对象做本类数据成员。称layeringclass(外层),layeredclass(内层)classAddress;classPhoneNumber;classPersonpublic:private:stringname;/layeredobjectAddressaddress;/layeredobjectPhoneNumbervoicNumber;/layeredobjectPhoneNumberfaxNumber;/layeredobject;,称Person类铺陈(layered)在string,Address,PhoneNumber之上。layering=composition,containment,embedding;layering意味着有一个has-a或根据某物实现(is-implemented-in-termsof).如何区分isa和has-a?,C+标准库有一个set模板类,要求set的元素类型T是整型,可以编号排序。如果需要做一个set其元素类型T不能排序,怎么办?可以用list来实现set?list是T的链表类型。templateclassSet:publiclist;实际上完全错误。因为链表中元素可以重复出现,而集合不允许。正确的方法是,用链表做成员,重新设计一个集合类型即可。,templateclassSetpublic:boolmember(constT,templateboolset:member(constT,templatevoidset:remove(constT注意:采用layering技术使两个class有了编译依赖关系,最好采用34条方法加以改进。,41区分继承和模板,两个例子:信息系学生学习C+,想自行设计stack,intstack,stringstack等等。一个猫迷想设计一些catclasses表现不同品钟的猫。必须先问自己一个问题,不同的类型T会不会影响class的行为。如有影响,必须用虚函数,从而要用继承机制。如不影响可以用模板。不同类型的stack都有相同的行为。push,pop,peek,empty等等,用template再简单不过了。而不同的猫有不同的特性,不同的吃睡本领。用继承最好。,42明智地运用私有继承,classPerson;classStudent:privatePerson/私有继承;voiddance(constPerson/错误!student不是Person对象,私有继承,基类中的公有,和保护成员在派生类中都变成私有成员。采用私有继承,并不希望isa,即导出类对象不再是基类对象。基类中私有数据成员在导出类中不可见。公有和保护成员函数的实现,可以被导出类成员函数采用。尽量不用私有继承,除非必要。,模板类的每一个具体类型化,都会产生一份代码。十个类型便得到十份完整代码。“因template导致程序代码膨胀”。可以用泛型指针来避免代码膨胀。把模板stack改为非模板stackclass.,classGenericStackprotected:GenericStack();GenericStack();voidpush(void*object);void*pop();boolempty()const;,private:structStackNodevoid*data;StackNode*next;StackNode(void*newData,StackNode,*NextNode):data(newData),next(nextNode);StackNode*top;GenericStack(constGenericStack,GenericStack类不能使用,把protected改成public可以用,但太容易出错了。泛型指针对任意指针都不加区别。可以用私有继承完成代码:classIntStack:privateGenericStackpublic:voidpush(int*intptr)GenerickStack:push(intptr);int*pop()returnstatic_cast(GenerickStack:pop();boolempty()constreturnGenericStack:empty();;,classCatStack:privateGenericStackpublic:voidpush(Cat*icatptr)GenerickStack:push(icatptr);Cat*pop()returnstatic_cast(GenerickStack:pop();boolempty()constreturnGenericStack:empty();;IntStackints;/没问题CatStackcs;/没问题,与layering技术一样,私有继承实现的代码,可以避免重复。还可以用模板私有继承改进,进一步避免太多的书写。templateclassStack:privateGenericStackpublic:voidpush(T*objectptr)GenerickStack:push(objectptr);int*pop()returnstatic_cast(GenerickStack:pop();boolempty()constreturnGenericStack:empty();;,这是一个通用Stack,可以具体实现任意类的Stack.如果你写错字,编译器会自动指出错误。由于GenericStack使用泛型指针,这段代码只付出一份拷贝的代价。因此,这个程序既安全又高效,很难做得更好。,43明智地运用多继承,多继承产生歧义(ambiguity),两个基类中有同名成员,派生类中必须指明其基类。由一个类派A生出两个B,C,再由B,C多继承派生出D,所谓钻石形,出现模棱两可歧义。用虚基类可以避免数据成员的模棱两可。但要避免将构造函数的参数传给虚基类。最好的办法是虚基类中不要有任何数据成员。Java就有这样的规定。但怎样处理虚函数呢?虚函数经由不同的路径产生模棱两可。因此要避免钻石形继承。但非钻石形多继承是有意义的。,两个基类一个是抽象类继承其接口,另一个继承其实现,有时有奇妙的作用。一个公有继承,一个私有继承。classPersonpublic:virtualPerson();virtualstringname()const=0;virtualstringbirthDate()const=0;virtualstringaddress()const=0;virtualstringnationality()const=0;,classDatabaseID;classPersonInfopublic:PersonInfo(DatabaseIDpid);virtualPersonInfo();virtualconstchar*thename()const;virtualconstchar*theBirthDate()const;virtualconstchar*theAddress()const;virtualconstchar*theNationality()const;virtualconstchar*valumeDelimOpen()const;virtualconstchar*valumeDelimClose()const;;,classMyPerson:publicPerson,privatePersonInfopublic:MyPerson(DatabaseIDpid):PersonInfo(pid)constchar*valumeDelimOpen()constreturn“”;constchar*valumeDelimClose()constreturn“”;stringname()constreturnPersonInfo:theName();stringbirthDate()constreturnPersonInfo:theBirthDate();stringaddress()constreturnPersonInfo:address();stringnationality()constreturnPersonInfo:theNationality();,43说出你的意思并了解你所说的每一句话杂项讨论45清楚知道C+编译器为我们完成和调用哪些函数46宁愿编译和连接时出错,也不要执行时出错47使用non-localstaticobjects之前确定它已有初值48不要对编译器的警告信息视而不见49尽量让自己熟息C+标准程序库50加强自己对C+的了解,classCartoonCharacter;/卡通形象classInsert:publicCartoonCharacter/昆虫public:virtualvoiddance();virtualvoidsing();protected:virtualvoiddanceCustomization1()=0;virtualvoiddanceCustomization2()=0;virtualvoidsingCustomization()=0;,classGrasshoper:publicInsert/蚱蜢protected:virtualvoiddanceCustomization1()=0;virtualvoiddanceCustomization2()=0;virtualvoidsingCustomization()=0;classCricket:publicInsert/蟋蟀protected:virtualvoiddanceCustomization1()=0;virtualvoiddanceCustomization2()=0;virtualvoidsingCustomization()=0;,44说出你的意思并了解你所说的每
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 标准房屋租赁合同条款及注意事项
- 2025建筑安全员b证考试题库附及答案解析
- 新能源行业2025人才流动动态洞察行业发展趋势报告
- 2025年成人高等学校招生考试《语文》语言得体表达与口语交际测试题库
- 2025年高压电工安全事故应急处理预案编制与评估试题库
- 企业内部保密协议及信息安全管理
- 2025年机械安全操作规范初级职称考试题库
- 2025年消防培训考核题库:消防法律法规与消防器材使用试题
- 2025年护士试题库附真题模拟及解析
- 2025年消防安全知识培训考试题库:消防员职业道德与消防安全意识测试试卷
- 自带设备管理办法
- 2025年天津港校招笔试题目及答案
- 工程图学发展史
- 初一初二心理健康讲座
- 2025年二建《建筑实务》真题答案及解析
- 光学相干断层扫描(OCT)在眼科诊断中的应用考核试卷
- 消防设备供货质量保证措施
- 正常产褥 教学课件
- 中药煎药培训课件
- 2025保安证考试试题及答案集合
- 中国高尿酸血症与痛风诊疗指南(2024年)
评论
0/150
提交评论