高质量C/C++编程指南--读书笔记七 7、8章

第七章  内存管理
 
一、内存管理
1、内存使用规则:(P45)
(1)、用malloc 或 new 申请内存之后,应该立即检查指针值是否为NULL 。防止使用指针值为 NULL 的内存。
(2)、不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
(3)、避免数组或指针的下标越界,特别要当心发生“多1” 或者 “少1” 操作。
(4)、动态内存的申请和释放必须配对,防止内存泄露。
(5)、用free 或 delete 释放了内存之后,立即将指针设置为NULL,防止产生“野指针”
 
2、指针与数组:
#include
using namespace std;
 
int main(){
     char str1[] = “abc”;
     char str2[] = "abc";
     const char str3[] = "abc";
     const char str4[] = "abc";
     const char * str5 = "abc";
     const char *str6 = "abc";
     char * str7 = "abc";
     char *str8 = "abc";
 
     cout << (str1 == str2) << endl;
     cout << (str3 == str4) << endl;
     cout << (str5 == str6) << endl;
     cout << (str6 == str7) << endl;
     cout << (str7 == str8) << endl;
     return 0;
}
数组str1、str2、str3和str4 都是在栈中分配的,内存中的内容都是“abc” 加一个“\0”,但是它们的存储位置不同,因此前两个输出0。
指针str5、str6、str7和str8也是在栈中分配的,它们都指向“abc”字符串,注意“abc” 存放在数据区,所以str5、str6、str7和str8指向同一块数据区的内存,因此,后三行输出为1。
 
3、字符数组中的内容是可以修改的,应该那些内容是保存在栈区的,而指针指向常量字符串,则不能通过指针来修改常量字符串的内容。(P46)
 
4、如果函数的参数是一个指针,不要指望用该指针去申请动态内存。如下:Test 函数的语句 GetMemory(str, 200)并没有使 str 获得期望的内存,str 依旧是 NULL:

毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数 p 的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p 的内容,就导致参数 p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请了新的内存,只是把_p 所指的内存地址改变了,但是 p 丝毫未变。所以函数 GetMemory并不能输出任何东西。事实上,每执行一次 GetMemory 就会泄露一块内存,因为没有用free 释放内存。 
按照编译器为函数参数制作副本的概念,如果非要用指针参数去申请内存,则应该用“指向指针的指针”,这样就可以修改参数的内容了。(P48)

5、也可以用函数返回值来传递动态内存:(P48)

但是用函数返回值传递内存这种方法长被人误用,把return语句用错了。这里强调的是不要用return语句返回指向“栈内存”的指针。因为该内存在函数结束时已经自动消亡了。

上面两个例子的区别就在于申请的内存位置不同,导致前者正确,后者错误。
 
6、关于free和delete,使用它们把指针内存释放掉了后,指针的地址仍然没变,只是该地址对应的内存时垃圾(地址没变但不是原来保存的内容了),p编程了“野指针”。如果此时不把p设置为NULL,会让人误以为p是个合法的指针。(P50)
 
7、指针的一些特征:(P50)
(1)、指针消亡了,并不表示它所指的内存会被自动释放。动态分配的必须手动释放。
(2)、内存被释放了,并不表示指针会消亡或者成了NULL指针。
 
8、“野指针”不是NULL指针,而是指向“垃圾”内存的指针。“野指针”的成因主要有两种:(P51)
(1)、指针变量没有被初始化;
(2)、指针p被free或者delete之后没有置为NULL,让人误以为p 是个合法的指针。
 
9、malloc 与 free 是C/C++语言的标准库函数,new / delete 是C++ 的运算符。它们都可以用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。 (P52)
 
10、内存耗尽的处理办法:(P 54)
(1)、判断指针是否为NULL,如果是则马上用return 语句终止本函数。
(2)、判断指针是否是NULL,如果是则马上用exit(1) 终止整个程序的运行。
如果一个函数内有多处需要申请动态内存,那么方式(1)就显得力不从心(释放内存很麻烦),应该用方式(2)来处理。
 
11、函数malloc的原型如下:(P 55)
void *malloc(size_t size);
int *p= (int*)malloc(sizeof(int)*length);
我们应该把注意力集中在两个要素上:“类型转换”和“sizeof”。
(1)、malloc 的返回值类型是void *,所以在调用malloc 时需要显式地进行类型转换,将void *转换成所需要的指针类型;
(2)、malloc 函数本身并不识别要申请的内存时什么类型,它只关心内存的总字节数。
 
12、函数free 的原型如下:(P55)
void free(void *memblock);
 
13、new 内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new 在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数,那么new 的语句也可以有多种形式。如果new 创建对象数组,那么只能用对象的无参数构造函数。(P56)
 
 
第八章
 
一、知识点
1、C++程序要调用已经被编译后的C函数,假设某个C函数的声明如下:(P58)
void foo(int x, int y);
该函数被C 编译器编译后在库中的名字为_foo,而C++ 编译器则会产生像_foo_int_int之类的名字用来支持函数重载和安全连接。由于编译后的名字不同,C++不能直接调用 C函数。C++提供了一个C链接交换指定符号 extern “C” 来解决这个问题。
 
2、成员函数的覆盖是指派生类函数覆盖基类函数,特征是:(1)、不同的范围;(2)、函数名字相同;(3)、参数相同;(4)、基类函数必须有virtual关键字。(P60)
 
3、如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual 关键字,基类的函数将被隐藏。
如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏。(P61)
 
4、参数的缺省值:(P63)
(1)、参数缺省值只能出现在函数的声明中,而不能出现在定义体中。
(2)、如果函数有多个参数,参数只能从后向前挨个儿缺省,否则将导致函数调用语句怪模怪样。 例如:
正确的示例如下:
void Foo(int x, int y =0, int z=0){};
 
错误的示例如下:
void Foo(int x =0, int y, int z =0){};
 
5、在 C++ 语言中,可以用关键字 operator加上运算符来表示函数,叫做运算符重载。(P65)
(1)、  如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符。 
(2)、  如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数。 

6、宏代码与内联函数:(P66)
(1)、预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL 调用、返回参数、执行 return 等过程,从而提高了速度。使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应
(2)、对于任何内联函数,编译器在符号表里放入函数的声明(包括名字、参数类型、返回值类型) 。如果编译器没有发现内联函数存在错误,那么该函数的代码也被放入符号表里。在调用一个内联函数时,编译器首先检查调用是否正确(进行类型安全检查,或者进行自动类型转换,当然对所有的函数都一样) 。如果正确,内联函数的代码就会直接替换函数调用,于是省去了函数调用的开销。这个过程与预处理有显著的不同,因为预处理器不能进行类型安全检查,或者进行自动类型转换。假如内联函数是成员函数,对象的地址(this)会被放在合适的地方,这也是预处理器办不到的。 
综上,C++  语言的函数内联机制既具备宏代码的效率又增加了安全性,而且可以自由操作类的数据成员
 
7、关键字inline 必须和函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。所以说,inline 是一种“用于实现的关键字” 。定义在类声明之中的成员函数将自动地成为内联函数。(P67)
 
posted on 2012-05-01 09:37  谷堆旁边  阅读(271)  评论(0编辑  收藏  举报