看《C++必知必会》比看《C++编程思想》编程思想有趣多了,《C++编程思想》是为C程序员写的,而我基本上一来就直接学的C++,因而《C++编程思想》并不适合我。而《C++必知必会》是直接从实际出发,直接点出来在编程中遇到的各种问题,因而很是实用,深得我心。
1.数据结构
摘要:为类型选择一个描述性名字。如果难以为这个类型命名,那就说明你还不知道你想要实现什么。
评:很是精辟,每当我想到了这个类的名字,我就知道了如何去做和做些什么。摘要:列举类型所能执行的操作。......要避免在实现时简单地为数据成员提供一串get/set操作——那不叫做数据抽象而是懒惰且缺乏想象力的表现。
评:不太明白,在我看来,C++中的类型仅仅是把数据按照一定的规律(类型)进行封装,其目的就是为了进行数据之间的交换。我的确不仅仅是用了一串get/set操作,但不是的操作也仅仅比get/set有一点延伸,比如按照某个条件来get/set一个或一组数据。
2.多态
摘要:基类可以不知道除自身以外的任何事物。
评:很是恰当,基类可以看成派生类的接口,派生类可以看成是基类的实现。
3.设计模式
摘要:一旦设计完成后,甚至你的经理都能够理解完整的设计方案,只要他具备一些必需的模式方面的知识。
评:以前我一直没有看过关于设计模式方面的书,也不理解什么是设计模式和为什么需要设计模式。现在,通俗的话说设计模式是程序员之间的“黑话”,它与使用的平台、语言毫无关系,它指的是你在制作程序时的一种构想或方法,而这些构造方法都是大家熟知的、经过验证的、有效的、高效率的。
4.STL
摘要:STL包含三大组件:容器、算法和迭代器。......STL的优秀思想体现在:容器与在容器上执行的算法之间无需彼此了解,这种戏法是通过迭代器实现的。
评:上面已经很好的解释了什么是STL了,而且还道出了C++中的精髓,无需彼此了解,不是侵入式的设计,还象那个基类、派生类。
5.引用
评:我只会把引用用在函数形参上,一般还给它加上一个const,其他的地方么,我一般使用指针。
6.数组形参
摘要:数组在传入时,实质上只传入指向其首元素的指针。
评:要小心!书中用一个准确的词“退化“来描述这种状况。不过自从我使用了vector后也就找不出什么理由使用数组了。我很高兴是这样的结果。什么,你说你一定要用多维数组,那么我说,你去死吧,我一般用一维代替多维,或者使用vector进行镶套。
7.产量指针和指向产量的指针
评:我背下了一个简单的方法来区别这两个。我把*和它前面的作为一组,*后面的作为一组,如const T *ptr,读后面的部分,ptr是指针,指向const T,又如T *const ptr,读为一个const指针,指向T类型。不过话说回来,我一直没有用过,也还没有想到使用的理由。
8.指向指针的指针
评:对这一点我一直提不起兴趣,讨厌这么复杂的冬冬。不过在实际中渐渐明白。如 vector my_circle; vector::iterator p=my_circle.begin(); for(;p!=my_circle.end();p++) {(*p)->draw();}
9.新式转型操作符
摘要:更丑陋,更难用,并且威力较小。
评:看到了这一章,我心里面咯噔了一下。我的程序里面还有那么野蛮的、不讲理的转换。我得尽快用这种更丑陋的方式去改过来,以减少错误的可能性。要注意static_const和dynamic_cast;
10.常量成员函数含义
摘要:
class X{
Public: void modify_buffer(int index,int val) const //不德!!!!!! { Buffer[index] = val;} Private: int *buffer; };
评:的确是不道德的,const的函数偷偷的改了一个数据,没有发现因为没有修改class X中的对象。我得小心了,不干这种事情。
11.编译器会在类中放东西
摘要:如果一个类声明了一个或多个虚函数,那么编译器将会为该类的每一个对象插入一个指向虚函数表的指针。
摘要:如果使用了虚拟继承,对象将会通过嵌入的指针、嵌入的偏移或其他的信息来保持对其虚基类子对象位置的跟踪。
摘要:一个POD(“plain old data”)非常重要。比如int、double、C struct、union都是POD。摘要:如果希望复制一个类对象,那么永远都不要使用memcpy这样的标准内存块复制函数。......相反,应该使用对象的初始化或者赋值操作。
评:看到最后这条我才明白为何有这章。看来对于高层的东西要使用高层的操作。
12.赋值和初始化并不相同
摘要:直截了当的说,赋值发生于当你赋值时,除此之外遇到的所有其他的复制情况均为初始化,包括声明,函数返回,参数传递以及捕俘描述异常中的初始化。
评:突然想起鼻祖书中的话,赋值是对一个结构良好的存储区去做,而初始化是对一个未定义的存储区去做。如果要赋值,那么就确立一下赋值的对象是否还是磁盘上的荒芜地带。
13.复制操作
摘要:复制构造和复制赋值是两种不同的操作。
14.函数指针
摘要:将一个函数的地址初始化或赋值给一个指向函数的指针时,无需显示的取得函数地址。
例子:void (*fp)(int);
extern void h(int);
fp = h; //OK
fp = &h; //OK
摘要:为了调用函数指针所指向的函数,而对指针进行解引用操作也是不必要的。
例子:(*fp)(12); //显示调用
fp(12); //隐式调用
摘要:函数指针的一个传统用途是实现回调(callback)。
评:一个很好的例子就是windows中对左右手的变化,如果改变了就只需要交换一下函数指针就OK了。
15.指向类成员的指针并非指针
摘要:“指向类成员的指针”即不包含地址,行为也不像指针。通常看作一个偏移量。
摘要:指向数据成员的指针对于描述“逆变性”的概念很方便。
摘要:存在指向基类成员的指针到指向公有派生类成员的指针隐式转换,反之不行。
评:明白了它的作用却不知道哪里有用,完全可以换个方式使用,即使是为了上一章的回调。如果要得到类中的成员,完全可以用类的对象或指向类对象的指针获得,这个可能是为了C程序员吧。
tip:不能想着指向static成员,它们并不是存在于所有类对象的一个偏移量。使用的方式和普通的数据一样。
16.指向成员函数的指针并非指针
摘要:NULL
17.处理函数和数组声明
摘要:
int *f1(); //一个返回值为int *的函数
int (*fp1)(); //一个指针,指向一个返回值为int的函数
int *a1[N]; //一个具有N个int *元素的数组
int (*ap1)[N]; //一个指针,指向一个具有N个int元素的数组
评:够复杂吧,不过看了下面的就要。。。。。。
摘要: int(*af2[N])(); //一个具有N个元素的数组
//其元素类型指向返回值为int的函数指针
评:够吓人的,虽然有解决法子可我没在意。因为老早就决定不用数组了。
18.函数对象
摘要:函数对象也是一个普通的类对象,通过重载函数调用操作符()来创建类似于函数指针的东西。
评:这可是个好东西,在泛型算法中作为谓词。
19.Command模式与好莱坞法则
摘要:好莱坞法则即“不要call我们,我们会call你”。
摘要:将一个函数对象于好莱坞法则相结合,即为Command模式的一个实例。
摘要:好处是,函数对象可以封装数据,另一个好处是函数对象可以通过虚拟成员表现出动态行为,第三个好处是处理类层次结构而不是较为原始的,缺乏灵活性的结构(例如函数指针)。
20.STL函数参数
摘要:NULL
21.重载与重写并不相同
摘要:重载发生于同一个作用域内有两个或更多个函数具有相同的名字但签名不同时。
摘要:重写发生于派生类函数和基类有相同的的名字和签名时。
22.Template Method 模式
摘要: Template Method(模板方式)模式和C++模板一点关系都没有。实际上,它是基类设计者为派生类设计者提供清晰指示的一种方式,这个指示就是“应该如何实现基类所规定的契约”。
摘要:一个基类的成员函数是否应该为非虚拟的、虚拟的或纯虚拟的,这样的决策主要是基于该函数的行为如何被派生类定制。
摘要:如果基类成员是非虚拟的,那么基类设计者就是以该基类为所确立的层次结构指明了一个不变式。派生类不应该用同名的派生类成员去隐藏基类非虚函数。
摘要:虚函数和纯虚函数指定的操作,其实现可以由派生类通过重写机制定制。一个非纯虚函数提供了一个默认实现,并不强迫派生类一定要重写它,而一个纯虚函数则必须在具体派生类中进行重写。
评:这一章对于我们如何定义基类有了一个很好的说明。函数是具体的实现,而我们确定如何具体的实现函数。
Tip:另外有一种派生类作为基类接口的形式,那里的法则就不太一样了。基类是实现,派生类是为了给其他用户的接口。
23.名字空间
摘要:本质上,名字空间是对全局作用域的细分。
摘要:许多C++程序员建议将using指令放在全局作用域中,这是个馊注意。
评:一定要很好的区分什么是using指令和using声明。
Using namespace namespace_name //是指令
Using anamespace_name::名字空间声明的的东西 //是声明
如果在全局作用域中使用using指令那等于去掉了名字空间的作用域。这里一般指的是程序员自己定义的名字空间,不是默认的std。
24.成员函数查找
摘要:调用一个成员函数时,涉及三个步骤:第一步,编译器查找函数的名字;第二步,从可用候选者中选取最佳匹配函数;第三步,检查是否具有访问该匹配函数是权限;
评:这里面隐含的说了一个冬冬,如果编译器找到了函数的名字,它是不会再去找的了。如果无权限范围该函数,那么就会在第三步出现编译错误。
25.实参相依的查找
摘要:ADL(实参相依的查找)指的是,当查找一个函数调用表达式中的函数名字时,编译器也会到“包含函数调用实参的类型”的名字空间查找。
评:我却好像记得不止是名字空间,也包括class,因为class其实也是一种特殊的名字空间。回头还得再看一遍鼻祖的书,那里面其实都有,只是没有重点标出来而已。
26.操作符函数查找
摘要:当使用函数调用语法时,应用的是普通的查找规则(ADL)。而对重载操作符的中缀调用的处理机制不同。
例子:
class X{ X operator%(const X&)const;};
X x,y;
x % y; //中缀调用
x.operator%(y); //成员函数调用
摘要:对于中缀操作符调用来说,编译器不仅会考虑成员操作符,也会考虑非成员操作符。
评:说的有点让人迷糊。我想主要注意使用.operator时,记得有个默认的*this实参。
27.能力查询
摘要:能力查询只是偶尔需要,但它们往往被过渡使用。它们通常是糟糕设计的“指示器”。最好避免对一个对象的能力进行运行期查询。
评:能力查询指的是对一个类对象进行dynamic_cast,来知道它是否是另外的类型,通常,是横向转换而不是普通的向上或者向下。
28.指针比较的含义
摘要:指针比较不是关于地址的问题,而是关于对象同一性的问题。
摘要:一个非常重要的经验,处理指向对象的引用或指针时,必须小心避免丢失类型信息(如把指针赋值给void*指针)。
评:一个基类的指针是与其派生类的指针==的,并不是因为地址相同而是类型相同,因为派生类就是基类,就像班长就是学生一样。这里比较的是基类的类型。
29.虚构造函数与Prototype模式
评:构造函数是不能虚的,而这里指的是具有这样功能的函数。如在一个类中,我们使用一个成员函数clone来调用复制构造函数new X(*this),我的经验无法告诉我为何需要,不过有一点是很明确的,这个例子证明了软件设计的“不知情”模式。
30.Factory Method模式
评:一个沉重的打击,我在28、29看见了什么是比较高级的构架,也让我想起了别来call我,需要时我来call你的好莱坞模式。每个类都明白自己做什么,而你只是在问它们一个很大众的问题,而不是很私人的问题。重点推荐,我得好好看看,认真感受。如果能很明确的使用,我的认识将能够上一个档次。
31.协变返回类型
摘要:协变的优势在于,总是可以在适当程度的抽象层工作。如果我们是处理Shape,获得一个抽象的ShapeEditor;如果在处理某种具体的形状类型,比如Circle,我们就可以直接获得CircleEditor。协变机制使得我们可以不使用类型转换操作来“重新”提供类型信息,而这种信息是一开始就不应该丢掉的。
32.禁止复制
摘要:访问修饰符(public、protect、private)可以用于表达和执行高级约束技术,指明一个类可以被怎样使用。这些技术中最常见的一种是不接受对象的复制操作,这是通过将其复制操作声明为private同时不为之提供定义而做到的。
评:有时候我们应该把所有不想给其他人使用的函数全部放进private。
(因为后面的比较深,暂时作罢。勉强看也只能懂个浮浅的东西。未完待续)