C++编程,对于开发者,均可以写上二段,可是真正能写出高质量的代码估计仍是比较少,一样我也是学习者,本文做为平时学习日志吧。随时都会更新……程序员
一、const 数据成员只在某个对象生存期内是常量,而对于整个类而言倒是可变的, 由于类能够建立多个对象,不一样的对象其const 数据成员的值能够不一样 第6 章 函数设计 一、函数接口的两个要素是参数和返回值。 避免函数有太多的参数,参数个数尽可能控制在5 个之内。 尽可能不要使用类型和数目不肯定的参数。 二、在函数体的“入口处”,对参数的有效性进行检查。 不少程序错误是由非法参数引发的,咱们应该充分理解并正确使用“断言”(assert)来防止此类错误。 断言assert 是仅在Debug 版本起做用的宏,它用于检查“不该该”发生的状况。 assert((pvTo != NULL) && (pvFrom != NULL)); 三、在函数体的“出口处”,对return 语句的正确性和效率进行检查。 若是return 语句写得很差,函数要么出错,要么效率低下。 (1)return 语句不可返回指向“栈内存”的“指针”或者“引用”,由于该内存在函数体结束时被自动销毁。 四、引用与指针的比较 引用的一些规则以下: (1)引用被建立的同时必须被初始化(指针则能够在任什么时候候被初始化)。 (2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则能够是NULL)。 (3)一旦引用被初始化,就不能改变引用的关系(指针则能够随时改变所指的对象) 引用的主要功能是传递函数的参数和返回值。 C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。 五、指针可以毫无约束地操做内存中的如何东西,尽管指针功能强大,可是很是危险。 第7 章 内存管理 一、内存分配方式有三种: (1) 从静态存储区域分配。 内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。 (2) 在栈上建立。在执行函数时,函数内局部变量的存储单元均可以在栈上建立,函数执行结束时这些存储单元自动被释放。 栈内存分配运算内置于处理器的指令集中,效率很高,可是分配的内存容量有限。 (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存, 程序员本身负责在什么时候用free 或delete 释放内存。动态内存的生存期由咱们决定,使用很是灵活,但问题也最多。 二、常见的内存错误及其对策 (1)内存分配未成功,却使用了它。 经常使用解决办法是,在使用内存以前检查指针是否为NULL。 若是指针p 是函数的参数,那么在函数的入口处用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。致使产生“野指针”。 三、用malloc 或new 申请内存以后,应该当即检查指针值是否为NULL。防止使用指针值为NULL 的内存。 不要忘记为数组和动态内存赋初值。防止将未被初始化的内存做为右值使用。 避免数组或指针的下标越界,特别要小心发生“多1”或者“少1”操做。 动态内存的申请与释放必须配对,防止内存泄漏。 用free 或delete 释放了内存以后,当即将指针设置为NULL,防止产生“野指针”。 四、指针与数组的对比 C++/C 程序中,指针和数组在很多地方能够相互替换着用。 数组要么在静态存储区被建立(如全局数组),要么在栈上被建立。数组名对应着(而不是指向)一块内存。 指针能够随时指向任意类型的内存块,它的特征是“可变”,因此咱们经常使用指针来操做动态内存。 char *p = “world”;指针p 指向常量字符串“world”(位于静态存储区,内容为world),常量字符串的内容是不能够被修改的。 从语法上看,编译器并不以为语句p[0]= ‘X’有什么不妥,可是该语句企图修改常量字符串的内容而致使运行错误。 五、C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。 注意当数组做为函数的参数进行传递时,该数组自动退化为同类型的指针。 六、指针参数是如何传递内存的? 若是函数的参数是一个指针,不要期望用该指针去申请动态内存。 七、编译器老是要为函数的每一个参数制做临时副本,指针参数p 的副本是 _p,编译器使 _p = p。 若是函数体内的程序修改了_p 的内容,就致使参数p 的内容做相应的修改。这就是指针能够用做输出参数的缘由。 在本例中,_p 申请了新的内存,只是把_p 所指的内存地址改变了,可是p 丝毫未变。因此函数GetMemory 并不能输出任何东西。事实上,每执行一次GetMemory 就会泄露一块内存,由于没有用free 释放内存。 八、若是非得要用指针参数去申请内存,那么应该改用“指向指针的指针“。 因为“指向指针的指针”这个概念不容易理解,咱们能够用函数返回值来传递动态内存。 这里强调不要用return 语句返回指向“栈内存”的指针,由于该内存在函数结束时自动消亡。 九、发现指针p 被free 之后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p 成了“野指针”。 若是此时不把p 设置为NULL,会让人误觉得p 是个合法的指针。 十、动态内存会被自动释放吗? (1)指针消亡了,并不表示它所指的内存会被自动释放。 (2)内存被释放了,并不表示指针会消亡或者成了NULL 指针。 十一、“野指针”不是NULL 指针,是指向“垃圾”内存的指针。 “野指针”的成因主要有两种: (1)指针变量没有被初始化。任何指针变量刚被建立时不会自动成为NULL 指针,它的缺省值是随机的, 它会乱指一气。因此,指针变量在建立的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。 (2)指针p 被free 或者delete 以后,没有置为NULL,让人误觉得p 是个合法的指针。 (3)指针操做超越了变量的做用范围。 十二、有了malloc/free 为何还要new/delete ? 一、 malloc 与free 是C++/C 语言的标准库函数,new/delete 是C++的运算符。 二、对于非内部数据类型的对象而言,光用maloc/free 没法知足动态对象的要求。对象 在建立的同时要自动执行构造函数, 对象在消亡以前要自动执行析构函数。 1三、内存耗尽怎么办? 若是在申请动态内存时找不到足够大的内存块,malloc 和new 将返回NULL 指针, 宣告内存申请失败。一般有三种方式处理“内存耗尽”问题。 (1)判断指针是否为NULL,若是是则立刻用return 语句终止本函数。 (2)判断指针是否为NULL,若是是则立刻用exit(1)终止整个程序的运行。 (3)为new 和malloc 设置异常处理函数。 1四、int *p = (int *) malloc(sizeof(int) * length); 若是p 是NULL 指针,那么free 对p 不管操做多少次都不会出问题。若是p 不是NULL 指针,那么free 对p 连续操做两次就会致使程序运行错误。 int *p2 = new int[length];由于new 内置了sizeof、类型转换和类型安全检查功能。 1五、个人经验教训是: (1)越是怕指针,就越要使用指针。不会正确使用指针,确定算不上是合格的程序员。 (2)必须养成“使用调试器逐步跟踪程序”的习惯,只有这样才能发现问题的本质。 第8 章 C++函数的高级特性 一、对比于C 语言的函数,C++增长了重载(overloaded)、内联(inline)、const 和virtual四种新机制。 其中重载和内联机制既可用于全局函数也可用于类的成员函数,const 与virtual 机制仅用于类的成员函数。 二、成员函数被重载的特征: (1)相同的范围(在同一个类中); (2)函数名字相同; (3)参数不一样; (4)virtual 关键字无关紧要。 覆盖是指派生类函数覆盖基类函数,特征是: (1)不一样的范围(分别位于派生类与基类); (2)函数名字相同; (3)参数相同; (4)基类函数必须有virtual 关键字。 三、运算符重载 在C++语言中,能够用关键字operator 加上运算符来表示函数,叫作运算符重载。 Complex Add(const Complex &a, const Complex &b); Complex operator +(const Complex &a, const Complex &b); 运算符与普通函数在调用时的不一样之处是:对于普通函数,参数出如今圆括号内;而对于运算符,参数出如今其左、右侧。 四、函数内联 C++ 语言支持函数内联,其目的是为了提升函数的执行效率(速度)。 定义在类声明之中的成员函数将自动地成为内联函数. 内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提升函数的执行效率。 每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。 第9 章 类的构造函数、析构函数与赋值函数 一、根据经验,很多难以察觉的程序错误是因为变量没有被正确初始化或清除形成的,而初始化和清除工做很容易被人遗忘。 第11 章 其它编程经验 一、const 更大的魅力是它能够修饰函数的参数、返回值,甚至函数的定义体。 二、对于非内部数据类型的输入参数,应该将“值传递”的方式改成“const 引用传递”,目的是提升效率。 例如将void Func(A a) 改成void Func(const A &a)。 对于内部数据类型的输入参数,不要将“值传递”的方式改成“const 引用传递”。不然既达不到提升效率的目的, 又下降了函数的可理解性。例如void Func(int x) 不该该改成void Func(const int &x)。 三、任何不会修改数据成员的函数都应该声明为const 类型。 四、变量(指针、数组)被建立以后应当及时把它们初始化,以防止把未被初始化的变量当成右值使用。