高质量C++/C编程指南》读书笔记--week2
上次所阅读的为该书的前五章部分,该次阅读的为第六,第七章,第八章的部分。
第六章:函数设计
函数为C++/C程序的基本功能单元,要注意函数设计的每点细微之处。
6.1参数的规则:
函数的参数需要填写完整,如果没有参数,则用void填充,且参数的命名要恰当,尽量不使用类型和数目不定的参数,顺序也要合理,尽量控制在5个以内。一般来说,将目的参数放在前面,而源参数放在后面。如果参数为指针且仅作输入用或以值传递的方式传递对象,应在类型前面加const,防止该指针在函数内被修改,提高效率。
6.2返回值的规则:
不能省去函数的类型说明,没有返回值时,应明确之声明为void类型。函数的名字与返回值类型在语义上不可冲突。若返回值为一个对象,则有时使用“引用传递”替代“值传递”可以提高效率。
6.3:函数内部实现的规则:
使用assert(断言)在函数体的入口处,对参数可以进行有效性的检查,若参数无效或非法,则可以直接终止程序并提示参数在哪里出错了。在函数的出口处,也需对return语句的正确性和效率进行检查,清晰返回的是值,指针,还是引用。
6.4函数设计建议:一个函数的功能尽量要单一,函数体的规模要小,控制在50行代码内,避免函数带有记忆功能,每一次相同的输入应该有相同的输出。
第七章:内存管理
寻找内存区的所有隐藏的地雷并谨慎清除。
7.1内存的分配方式:
从静态储存区域分配,即内存在程序编译时就已经分配好,在程序的整个运行期间都存在,如全局变量与static变量。
在栈上创建,函数内的局部变量储存单元都可以在栈上创建,函数运行结束时则将这些储存单元释放,高效但分配内存量有限。
从堆上分配(动态内存分配),使用new或者malloc语句申请的内存,使用与生存期由程序员自己决定,使用灵活。
7.2常见内存错误及其对策:
内存分配未成功却使用了他。对应这种错误,可以在使用该内存之前检查指针是否为NULL。
内存分配虽然成功,但是却未初始化就引用。对应这种错误,在创建数组后,无论如何都要赋予一个初始值,即使是赋予零值。
内存分配成功并且已经初始化,但操作越过了内存边界。对应这种错误,谨慎注意数组的下标以及循环语句的循环次数。
忘记了释放内存,造成内存泄漏。对应这种错误,动态内存的申请与释放必须匹配,即malloc(new)语句与free(delete)语句的使用次数一定要相同。
释放了内存却继续使用它。对应这种错误,需注意return语句返回的是不是栈内存的指针
,使用free或delete释放内存后,需将指针设为NULL,避免产生野指针。最惨的情况,内存管理混乱,则需要重新设计数据结构。
7.3指针与数组:
数组中的字符串的内容可以修改,但是指针指向的一个常量字符串则不可以改变。使用sizeof计算内存量时,数组a与指向a的指针p,所得的结果不同,sizeof(p)所得的是一个指针变量的字节数。当数组作为函数参数进行传递时,数组将自动退化为同类型的指针,此时使用sizeof函数所得的值为该类型指针变量的字节数。当函数的参数为一个指针时,不能用该指针去申请动态内存,因为申请动态内存时,所改变的为希望改变的指针的副本,对希望改变的指针没有影响。
7.5指针与内存:
free与delete所释放的仅为指针所指向的内存,并没有删去指针,并且指针仍然指向原来所指向的地址,并不是NULL,成为了一个野指针。指针消亡了,并不表示所指向的内存会被自动释放,而内存被释放了,并不表示指针会消亡或者变成了NULL指针。
7.6 malloc/free与new/delete的区别与使用要点:
malloc/free不能执行构造函数与析构函数,malloc返回的值的类型为void,需要显性进行类型转换。而new/delete则可以完成对动态对象的内存管理,用delete释放数组函数时不能丢弃了符号 []。为了提高程序的可读性与降低出错率,两队组合不能混用。
第八章:C++函数的高级特性
C++语言对比于C语言函数添加了重载,内联,const,virtual四种新机制。
8.1函数重载:
函数重载即可以将语义,功能相似的几个函数用同一个名字表示,便于记忆,可提高函数的易用性。编译器根据参数为每个重载函数产生不同的内部标识符用于区分同名函数。此时,应当注意仅输入一个数字而未指定类型充当参数时,隐式类型转换将导致重载函数产生二义性,进而使编译失败。当C++调用C函数时,由于C++中重载函数编译后名字不同,所以要加上extern“C”来解决调用C函数的问题。全局函数与局部函数不构成重载。
8.2成员函数的重载,覆盖与隐藏:
成员函数被重载的特征为包括在同一个类中,函数名字相同,参数不同,virutal关键字可有可无。而覆盖是指派生类函数覆盖基类函数,特征为分别位于派生类与基类,函数名字相同,参数相同,基类函数必须有virtual关键字。
隐藏以为着派生类的函数屏蔽了与其同名的基类函数,满足下面规则:
如果派生类的函数与基类函数同名,但是参数不同,则无论有无virtual关键字,基类的函数被隐藏。
如果派生类的函数与基类的函数同名,并且参数也相同,但基类没有virtual关键字。此时,基类的函数被隐藏。