1.函数内部实现规则
(1)在函数的入口处,对参数的有效性进行检查
(2)在函数的出口处,对return语句的正确性和效率进行检查。
注意:1)return语句不可返回指向栈内存的指针或者引用,因该内存在函数体结束时被自动销毁
2)要搞清楚返回的究竟是“值”,“指针”还是“引用”。
3)如果函数返回值是一个对象,要考虑return语句的效率
2.函数设计的其他建议
(1)函数的功能单一,不要设计多用途的函数
(2)函数体的规模要小,尽量控制在50行的代码之内
(3)尽量避免函数带有“记忆”功能,尽量减少使用static局部变量
(4)不仅要检查输入参数的有效性,还要检查通过其他突进进入函数体内的变量的有效性。
(5)用于出错处理的返回值一定要清楚,让使用者不容易忽视或误解错误情况
3.使用断言:函数一般分为debug版本和release版本,debug版本用于内部调试,release版本发行给客户使用
断言assert是仅在debug版本起作用的宏,它用于检查不应该发生的情况。
(1)使用断言捕捉不应该发生的非法情况
(2)在函数的入口处,使用断言检查参数的有效性(合法性)
(3)在编写函数时,要进行反复的考查,并且自问:“我打算做那些假定”,一旦确定了假定,就要使用断言对假定进行检查
(4)一般教科书都鼓励程序员们进行防错设计,但要记住这种编程风格可能会隐瞒错误。当进行防错设计时,如果不可能发生的事情的确发生了,要使用断言进行报警。
4.引用与指针的比较
(1)引用被创建的同时必须被初始化;指针可以在任何时候被初始化
(2)不能有NULL引用,引用必须与合法的存储单元关联;指针可以是NULl
(3)一旦引用被初始化,就不能改变引用的关系;指针可以随时改变所指的对象
5.内存分配方式:
(1)从静态存储区分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限
(3)从堆上分配,也称动态内存分配。程序在运行的时候用malloc或者new申请任意多少的内存,程序员自己负责在何时用free或者delete释放内存。动态才、内存的生存期由我们决定,使用非常灵活,但问题也最多。
6.常见的内存错误及其对策
(1)内存未分配成功,却使用了它。
在使用内存之前检查指针是否为NULL .如果指针P是函数的参数,name在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new 来申请内存,应该用if(p== NULL)或者if(p!= null)进行防错处理。
(2)内存分配虽然成功,但是尚未初始化就引用它:原因之一是没有初始化观念;二是误以为内存的缺省初值全部为零,导致引起初值错误。
(3)内存分配成功并且初始化,但操作越过了内存的边界
(4)忘记释放内存,造成内存泄露:动态内存的申请和释放必须配对,程序中malloc和free的使用次数一定要相同,否则肯定有错误(new和delete同理)
(5)释放了内存却继续使用它
1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
2)函数的return语句写错了,注意不要反悔只想栈内存的指针或者引用。
3)使用free或者delete释放了内存之后,没有将指针设置为NULL。导致产生野指针。
(6)用malloc和new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。
(7)避免数组或指针的下表越界,特别要当心发生多1或者少1的操作。
(8)动态内存的申请与释放必须配对,防止内存泄露
(9)用free和delete释放了内存之后,立即将指针设置为NULL,防止产生野指针。
free和delete只是把指针所指的内容给释放掉,但并没有把指针本身干掉。
7.不能对数组名进行直接复制与比较
8.c++语言没有办法知道指针所指的内存容量,除非在申请内存的时候记住它。
注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
9.指针you些似是而非的特征:指针消亡了,并不表示它所指的内存会被自动释放;指针被释放了,并不表示指针会消亡或者成为NULL指针
10.野指针:是指向垃圾内存的指针,而不是空指针
1)野指针产生的原因:指针变量没有被初始化,指针变量被free或者delete之后没有被置为NULL
2)指针操作超越了变量的作用范围
11.malloc和free是C ++/c语言的标准库函数,new和delete是C++的运算符。都可以用于申请动态内存和释放内存
对于非内部数据类型的对象而言,光用malloc和free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc和free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc和free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理和释放内存工作的运算符delete。
不要企图用malloc和free来完成动态对象的内存管理,应该用new和delete。由于内部数据类型的对象没有构造和析构的过程,对他们而言new和delete ,malloc和free是等价的。
既然new和delete的功能完全覆盖了malloc和free,为什么c++不把malloc和free淘汰出局呢?这是因为C++程序经常要调用C函数,而C 程序只能用malloc和free管理动态内存。如果用free释放new创建的动态对象,name该对象因无法执行析构函数而可能导致程序出错。如果用delete释放malloc申请的动态内存,理论上讲程序不会出错,但是该程序的可读性很差。所以new和delete必须配对适应,malloc和free也是一样的。
12.malloc和free的使用要点:
1)malloc返回值的类型是void *,所以在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型。
2)malloc函数本身并不识别要申请的内存是什么类型,它只关心内存总的字节数
13.new和delete的使用要点:
1)int *p = new int[length]。
2)new内置了sizeof,类型转换和类型安全检查功能。
对于非内部数据类型的对象而言,new在创建动态对象的同时完成初始化工作。如果对象有多个构造函数,那么new的语句也可以有多重形式。
如果用new创建数组只能调用对象的无参构造函数。
delete [] object;
这个才是正确的使用方式,如果去掉[]则只能删掉第一个对象。
14.重载与覆盖
成员函数被重载的特征:
(1)相同的范围(在同一个类中)
(2)函数名字相同
(3)参数不同
(4)virtual关键字可有可无
覆盖是指派生类函数覆盖积累函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)virtual关键字必不可少
15.令人迷惑的隐藏规则
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时无论是否有virtual关键字,基类的函数江北隐藏。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时基类的参数被隐藏。
16.参数的缺省值
(1)参数缺省值只能出现在函数的生命中,而不能出现在定义体重
(2)如果函数有多个参数,参数只能从后向前挨个儿缺省,否则导致函数调用语句怪异
17.不能重载的运算符
(1)不能改变C++内部数据类型的运算符
(2)不能重载'.',因为它在雷总对任何成员都有意义,已经成为标准用法
(3)不能重载目前C++运算符集合中没有的符号,如#,@,$等。原因是难以理解和难以确定优先级
(4)对已经存在的运算符进行重载时,不能改变优先级规则,否则会引起混乱
18.构造函数的初始化表
(1)如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数
(2)类的const常亮只能在初始化表里被初始化,因为它不能再函数体内用复制的方式来初始化
(3)类的数据成员的初始化可以采用初始化表或者函数体内赋值两种方式,这两种方式效率不完全相同。
非内部数据类型的成员对象应当采用第一种方式初始化,以获取更高效率
19。提高程序的效率
(1)不要一味地追求程序的效率,应当在满足正确性,可靠性、健壮性、可读性等质量因素的前提下,设法提高程序的效率
(2)以提高程序的全局效率为主,提高局部效率为辅
(3)在优化程序的效率时,应当先找出限制效率的瓶颈,不要在无关紧要处优化
(4)先优化数据结构和算法,再优化执行代码
(5)有时候时间效率和空间效率可能对立,此时应当分析那个更重要
(6)不要追求紧凑的代码,因为紧凑的代码并不能产生高效的机器码
20.建议
(1)变量被创建之后应当及时把它们初始化,以防止把未被初始化的变量当成右值使用
(2)当心变量的初值,缺省值错误,或者精度不够
(3)当心数据类型转换发生错误,尽量使用显示的数据类型转换
(4)当心变量发生上溢或者下溢,数组的下标越界
(5)当心忘记编写错误处理程序,当心错误处理程序本身有误
(6)当心文件IO有错误
(7)避免编写技巧性很高的代码
(8)不要涉及面面俱到,非常灵活的数据结构
(9)如果原油的代码质量比较好,尽量复用它。
(10)尽量使用标准库函数
(11)尽量不要使用与具体硬件或软件环境关系密切的变量
(12)把编译器的选择项设置为最严格的状态