Essential C++读书笔记

来源:互联网 发布:手机 同声翻译软件 编辑:程序博客网 时间:2024/05/01 13:54
第一章:C++编程基础
1.class的定义,一般来说分为两部分,分别写于不同的文件。其中之一是所谓的“头文件(header file)”,用来声明该class所提供的各种操作行为(operations)。另一个文件,程序代码文件(program text),则包含这些操作行为的实现内容(implementation)。
2.所谓命名空间(namespace)是一种将程序库名称封装起来的方法。通过这种方法,可以避免和应用程序发生命名冲突的问题(所谓命名冲突是指在应用程序内两个不同的实体(entity)具有相同的名称,导致程序无法区分两者。命名冲突发生时,程序必须等到命名冲突获得决议(resolved)之后,才得以继续执行)。命名空间像是在众多的可见范围之间竖起的一道道围墙。
3.数据类型决定了对象所能含有的数值范围,同时也决定了对象应该占用多少内存空间。
4.一般来说,将每个对象初始化是个好主意——即使初值只用来表示该对象尚未具有真正意义的值。
5.OR逻辑运算符(||)if(exp1 || exp2),如果exp1被评估为true,剩下的exp2就不需要被评估(此为所谓骤死式评估法)
6.AND逻辑运算法(&&)同上。只要exp1判断为false,整个表达式即为false。
7.当我们使用指针时,必须在提领它之前先确定它的确指向某对象。初始化指针时,赋 0.
第二章:面向过程的编程风格
1.将函数独立出来的做法可带来三个主要好处:第一,以一连串函数调用操作,取代重复撰写相同的程序代码;第二,我们可以在不同的程序中使用这些函数;第三,我们可以更轻易地将工作分配给协力开发团队。
2.如果函数的返回性别不为void,那么它必须在每个可能的退出点上将值返回。
3.当我们调用一个函数时,会在内存中建立起一块特殊区域,称为“程序栈(program stack)”这块特殊区域提供了每个函数参数的存储空间。它也提供函数所定义的每个对象的内存空间——我们将这些对象称为local object(局部对象)。一旦函数完成,这块内存就会被释放掉,或者说是从程序堆栈中被POP出来。当我们pass by value(传值)时,传入函数时,其值会被复制一份,称为参数的局部性定义(local definition)。
4.当我们以by reference(传引用)方式彻底对象当做函数参数,对象本身并不会复制出另一份——复制的是对象地址,reference扮演着外界与对象之间的一个间接号码牌的角色。将参数声明为reference的理由之一是,希望得以直接对所传入的对象进行修改,之个理由极为重要。理由之二是,为了降低复制大型对象的负担。(exp:传入参数数据量大时,复制需要花费大量时间)。
5.pointer参数和reference参数二者之间更重要的差别是,pointer可能(也可能不)指向某个实际对象。当我们提供pointer时,一定要先确定其值并非为 0。至于reference则必定会代表某个对象,所以不须做此检查。
6.除了一个必要的例外(static)之外,函数内定义的对象,只存活于函数执行之际。如果将这些所谓局部对象(local object)的地址返回,会导致执行器错误。还记得吗,函数乃临时位于程序栈(内存内的一块特殊区域)之上。局部对象被置于这块区域中。当函数执行完毕后,这块区域的内容便会被弃置,于是局部对象不复存在。一般而言,对根本不存在的对象进行寻址操作,是不好的习惯。
7.不论以pointer或reference形式将值返回,都不正确,因为局部变量在函数执行完毕时已不复存在。如果将变量以值方式返回,便不会产生任何问题,因为返回的乃是对象的复制品,它在函数之外依然存在。
8.对象如果在函数以外声明,则具有所谓的file scope。内建型别的对象如果定义在file scope之内,必定被初始化为0.但如果它们被定义于local scope之内,那么除非程序员指定其初值,否则不会被初始化。
9.不论local scope或file entent,对于我们而言,都是由系统自动管理。第三种存储期形式称为dynamic extent(动态范围)。其内存系有程序的自由空间(free store)配置而来,有时也称为heap memory(堆内存)。此种内存必须由程序员自行管理,其配置是通过new 表达式来达成的,而其释放则经由delete表达式完成。
10.局部静态对象所处的内存空间,即使在不同的函数调用过程中,依然持续存在。
11.一般而言,最适合声明为inline的函数:体积小,常被调用,所从事的计算并不复杂。inline函数的定义常常被置于头文件中。由于编译器必须在它被调用的时候加以展开,所以这个时候其定义必须是有效的。【将函数声明为inline,表示要求编译器在每个函数调用点上,将函数的内容展开】
12.“只定义一份”的规则有个例外:inline函数的定义。为了能够扩展inline函数的内容,在每个调用点上,编译器都能取得其定义。这意味着我们必须将inline函数的定义置于头文件,而不是把它放在各个不同的程序代码文件。
第三章:泛型编程风格
第四章:基于对象的编程风格

1.所有member function都必须在class主体内进行声明。至于是否要同时进行定义,可自由决定。如果要在class主体内定义,这个member function会自动地被视为inline函数。non-inline member function应该在程序代码文件中定义。
2.构造函数中成员初始化表主要用来将参数传给member class boject。
第五章:面向对象编程风格
1.在编译时期就决定调用对象--静态绑定(static binding)
2.在执行期(run-time)决定调用对象--动态绑定(dynamic binding)
3.继承特性让我们得以定义一整群互有关系的类,并共享共通的接口。多态则让我们得以用一种与型别无关(tgpe-independent)的方式来操作这些类对象。我们通过抽象基类的pointer和reference来操控其共通接口,而实际执行起来的操作则需等到执行期,依据pointer或reference所寻址之实际对象的型别才能决定。多态和多态绑定的特性,只是在使用pointer或reference时才发挥。
4、有哪些操作行为必须根据不同的派生类而有不同的实现方式,这些操作行为应该成为整个类继承体系中的虚拟函数(virtual functions)。
5.static member function无法被声明为虚拟函数。
6.一般来说,继承而来的public成员和protected成员,不论在继承体系中的深度为何,都可被视为派生类自身拥有的成员。
7.渐次测试自己的实现代码,这比整个程序都完成后才一次测试好多了。
8.reference永远无法代表空对象(null object),pointer却有可能是null。
9.data member如果是个reference,则必须在constructor的member initialization list中加以初始化。一旦初始化后,就再也无法指向另一个对象。如果data members是个pointer,就无此限制:我们可以在constructor内加以初始化,也可以先将它出书画为null,稍后再令指向某个有效的内存地址。在程序设计过程中我们便是根据这些不同的性质来决定要使用reference还是pointer。
10.“返回型别必须完全吻合”这一规则有个例外:当基类的虚拟函数返回某个基类形式(通常是pointer或reference),派生类的同名函数便可以返回该基类所派生出来的型别。
11.在两种情况下,虚拟函数机制不会出现预期行为:(1)在基类的constructor和destructor内;(2)当我们使用的是基类的对象,而非基类对象的pointer或reference。(对1解释:派生类中的data members尚未初始化。如果此时调用派生类的那一份虚函数,它便有可能取用为经初始化的data members,这可不是一件好事)。
12.在基类的constructor中,派生类的虚函数绝对不会被调用。
13.在C++中,唯有以基类的pointers和references才能够支持面向对象编程概念。
第六章:以template进行编程
1.建议将所有的template型别参数视为“class型别”来处理。这意味着我们会把它声明为一个const reference,而非以by value方式传递。
2.以pointer来传递,我们能够更改的是该pointer所指之物,而不是pointer本身。为了改变pointer本身,我们必须再加一层间接性。(e:BTnode *& prev)。
第七章:异常处理
1.在异常处理机制终结某个函数之前,C++保证,函数中的所有局部对象的destructor都会被调用。