(P?)头文件中应该只定义确实需要的东西。
(P37)反斜线符号必须是该行的尾字符――不允许其后面有注释或空格。同样,后继行行首的任何空格和制表符都是字符串字面值的一部分,正因如此,长字符串字面值的后继行才不会有正常的缩进。
(P42)直接初始化int ival(1024);、复制初始化int ival = 1024;与赋值ival = 1024;。初始化不是赋值。初始化是指创建变量并给它赋值。赋值是指擦除对象当前值并用新值代替。
(P44)在函数体外定义的变量都初始化成0,在函数体里定将的定义的内置类型不进行自动初始化。
(P50)一个非const变量定义在.cpp中,可以在另外的文件中使用这个变量(默认为extern),
如:
int counter;//file1.cpp
extern int counter;//file2.cpp
但const变量是定义该对象的文件的局部变量(默认为非extern),不能被其它文件访问,如:
const int counter = 3;//file1.cpp
extern int counter;//file2.cpp, error
如果把const变量定义时显示地指定为extern,就可以被其它文件访问,如:
extern const int counter = 3;//file1.cpp
extern const int counter;//file2.cpp
(P52)非const引用只能绑定到与该引用同类型的对象。Const引用可以绑定到不同但相关类型的对象或绑定到右值。
(P54)不能改变枚举成员的值。枚举成员本身就是一个常量表达式。
(P59)extern int ival = 10;有初始化式,所以是一个定义。Double ficarate;没有extern,所以也是一个定义。同一个程序中有两个以上文件含有上述任一个定义都会导致多重定义链接错误。
(P60)如果const变量不用常量表达式初始化,就不能在头文件中定义。它应该在源文件中定义,并通过在头文件中添加extern声明来共享。
(P73)string::size操作返回的是string::sizetype类型的值。任何存储string::size操作结果的变量必须为string::sizetype类型,不要把它赋给int型。string::sizetype实际是某种unsigned类型,它的存储范围是int的2倍。因此用int来接收可能会越界。(通过下标访问string中的字符时,也最好使用string::sizetype)
(P75)使用’+’对string对象和字符串字面值混合连接操作时,+的左右操作数至少有一个是string的。
(P79)vector对象(以及其它标准库容器对象)的重要属性就在于可以在运行时高效地添加元素。因为vector增长的效率高,在元素值已知的情况下,最好是动态地添加元素。
(P82)vector的循环遍历是这样的:for(vector
- 精确匹配。实参与形参类型相同。
- 通过类型提升实现的匹配(较小的整型提升为 int型)
- 通过标准转换实现的匹配
- 通过类类型转换实现的匹配
(P237)直接引用函数名等效于在函数名上应用取地址操作符,例如:
Typedef bool (cmpFcn)(const string &, const string &);
Bool lengthCompare(const string &, const string &);
cmpFcn pf1 = lengthCompare;
cmpFcn pf2 = &lengthCompare;
(P238)指向函数的指针可用于调用它所指向的函数。可以不需要使用解引用操作
符,直接通过指针调用函数,例如:
cmpFcn pf = lengthCompare;
lengthCompare("hi", "bye"); // direct call
pf("hi", "bye"); // equivalent call: pf1 implicitly dereferenced
(pf)("hi", "bye"); // equivalent call: pf1 explicitly dereferenced
(P238)函数指针作为形参有两种写法:
Void useBigger(const string &, const string &, bool(const string&, const string &));
Void useBigger(const string &, const string &, bool()(const string&, const string &));
(P238)函数可以返回指向函数的指针,但是,正确写出这种返回类型相当不容易:
int (ff(int))(int, int);
阅读函数指针声明的最佳方法是从声明的名字开始由里而外理解。
要理解该声明的含义,首先观察ff(int),将 ff声明为一个函数,它带有一个 int 型的形参。该函数返回int ()(int, int);它是一个指向函数的指针,所指向的函数返回 int型并带有两个分别是int 型和 int型的形参。
使用 typedef 可使该定义更简明易懂:
typedef int (PF)(int, int);
PF ff(int); // ff returns a pointer to function
(P239)允许将形参定义为函数类型,但函数的返回类型则必须是指向函数的指针,而不能是函数。例如:
typedef int func(int, int);
void f1(func); // ok: f1 has a parameter of function type
func f2(int); // error: f2 has a return type of function type
func f3(int); // ok: f3 returns a pointer to function type
(P245)如果函数有基类类型的引用形参时,可以给函数传递其派生类型的对象。例如对istream&进行操作的函数,也可使用ifstream或者istringstream对象来调用。因此IO类型通过继承关联,所以可以只编写一个函数,而将它应用到三种类型的流上:控制台、磁盘文件或者字符串流。代码:
void func(ostream & t)
{
}t<<"123"<<endl;
int main()
{
func(cout);
ofstream f("123.txt");
func(f);
return 0;
}
Sstream不知道怎么用
(P246)IO对象不允许复制和赋值操作,所以(1)流对象不能存储在容器中(2)形参或返回类型不能是流类型,但是可以是流类型对象的指针或引用
(P246)对IO对象的读写会改变它的状态,因此引用必须是非const的
(P248)逗号操作符的求解过程:首先计算它的每一个操作数,然后返回最右边操作数作为整个操作的结果。
(P250)如果需要使用最后的输出给程序错误定位,则必须确定所有要输出的都已经输出。为了确保用户看到程序实际上处理的所有输出,最好的方法是保证所有的输出操作都显式地调用了 flush或 endl。
(P250)当输入流与输出流绑在一起时,任何读输入流的尝试都将首先刷新其输出流关联的缓冲区。标准库将 cout与 cin 绑在一起,因此语句:标准库将 cout与 cin 绑在一起,因此语句:cin >> ival;导致 cout关联的缓冲区被刷新。
(P251)交互式系统通常应确保它们的输入和输出流是绑在一起的。这样做意味着可以保证任何输出,包括给用户的提示,都在试图读之前输出
(P252)IO标准库使用C风格字符串而不是C++strings类型的字符串作为文件名。假设要使用的文件名保存在string对象中,则可调用c_str成员获取 C 风格字符串。
(P265)尽管不能直接将一种容器内的元素复制给另一种容器,但可以通过传递一对迭代器间接实现该功能。使用迭代器时,不要求容器类型相同。容器内的元素类型也可以不相同,只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型,即可实现复制。
(P267)容器元素类型必须满足以下两个约束:(1)元素类型必须支持赋值运算。(2)元素类型的对象必须可以复制。引用不支持一般意义的赋值运算,因此没有元素是引用类型的容器。IO库类型不支持复制或赋值。因此,不能创建存放 IO 类型对象的容器。
(P268)必须用空格隔开两个相邻的 >符号,以示这是两个分开的符号,否则,系统会认为 >> 是单个符号,为右移操作符,并导致编译时错误。 vector< vector
vector | list | Deque | |
---|---|---|---|
Queue,要求提供push_front() | 不支持 | 支持 | 默认 |
stack | 支持 | 支持 | 默认 |
Priority_queue,要求提供随机访问 | 默认 | 不支持 | 支持 |
(P301)默认情况下,栈适配器建立在deque容器上。 (P308)“容器元素根据键的次序排列”这一事实就是一个重要的结论:在迭代遍历关联容器时,我们可确保按键的顺序的访问元素,而与元素在容器中的存放位置完全无关。 (P309)map所使用的键,必须在键类型上定义严格弱排序。即必须定义<操作符。 (P315)map使用下标是危险的:如果该键不在map中,则会插入一个具有该键的新元素。如果不要有插入操作,应使用count或find (P319)map是键-值对的集合。Set只是单纯的键的集合。当只想知道一个值是否存在时,使用set。 (P322)map和 set 容器中,一个键只能对应一个实例。而 multiset和 multimap 类型则允许一个键对应多个实例。Multimap不支持下标运算。 (P323)关联容器map和set中的元素是按顺序存放的,mulmap和mulset也一样。 (P337)指针的行为与作用在内置数组上的迭代器一样,因此对迭代器使用的算法也可以对数组使用。 (P338)泛型算法本身从不执行容器操作,只是单独依赖迭代器和迭代器操作实现。算法基于迭代器及其操作实现,而并非基于容器操作。 (P375)类不能具有自身类型的数据成员,但类的数据成员可以是指向自身类型的指针或引用。 (P381)形参表和函数体处于类作用域中,函数返回类型不一定在类作用域中 (P388)不管数据成员是否在构造函数初始化列表中显式地初始化,它总是会在初始化阶段初始化。初始化方法就是隐式地调用数据成员的默认构造函数。如果数据成员没有默认构造函数,会出错。 (P388)对于内置类型,使用初始化或赋值在结果和性能上是等价的,除了两个例外:const成员和引用。 (P389)初始化的顺序就是定义成员的顺序,与初始化列表的顺序无关 (P394)通过将构造函数声明为explicit来防止在需要隐式转换的上下文中使用构造函数。Explicit只用于类内部的构造函数上。 (P395)除了有明显的理由要定义隐匿转换,否则,单形参构造函数应该为explicit。 (P397)friend关键字只能出现在类定义的内部。友元不是授予友元关系的那个类的成员,所以它们不受声明出现部分的访问控制影响。 (P400)static成员函数没有this指针,不能声明为const或virtual (P401)static数据成员必须在类定义体外部定义(刚好一次),且在定义时初始化。 (P402)static数据成员可在字义体内部赋值,但是仍到在外部定义,但是定义时可以不初始化。 (P409)如果一个类中有数组成员,默认的复制构造函数会复制整个数组,即复制数组中的每一个元素。 (P410)为了防止复制,类必须显式地将复制构造函数声明为private。如果连友元和成员中的复制也要禁止,就声明一个private的复制构造函数但不定义。 (P411)声明而不定义成员函数是合法的,但使用它会导致链接失败。 (P411)如果定义了复制构造函数,也必须定义默认的构造函数。 (P411)赋值操作符的重载必须定义成类的成员函数。This绑定到指向左操作操作数。 (P431)重载操作符必须至少一个类类型或枚举类型的操作数 (P431)重载操作符的优先级、结合性、操作数不变。但不保存求值顺序。 (P435)输出操作符 <<的重载,操作符应接受 ostream& 作为第一个形参,对类类型 const对象的引用作为第二个形参,并返回对 ostream 形参的引用。例如: ostream& operator <<(ostream& os, const ClassType &object)。 (P436)输出操作符应输出对象的内容,进行最小限度的格式化,让用户自己控制输出细节。且它们不应该输出换行符。 (P437)由于类的成员函数的第一个参数一定是this,因此输出操作符 <<的重载不能做为成员函数。因此,类通常将 IO操作符设为友元。 (P438)使用流读入数据之前要检查。设计输入操作符时,如果可能,要确定错误恢复措施,这很重要。 (P441)类的赋值操作符必须为类的成员,以便编译器知道是否需要合成一个。 (P442)赋值操作符必须返回对this的引用 (P442)下标操作符必须定义为类成员函数 (P443)类定义下标操作符时,一般需要定义两个版本:一个为非const成员并返回引用;另一个为const成员并返回const引用。 (P443)箭头操作符(->)必须定义为成员函数。解引用操作符()无此要求 (P445)箭头操作符不接受显示形参。必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。 (P447)自增/自减操作符会改变操作对象的状态,因此更倾向于将它们作为成员。 (P447)前缘式操作符应返回被增/减对象的引用 (P447)为了区分自增/自减操作符的前缀和后缀形,后缀式接受一个额外的int形参 (P448)后缀操作符返回旧值,应作为值返回,不是返回引用。 (P450)函数调用操作符必须声明为成员函数。定义了调用操作符的类,其对象常称为函数对象。 (P454)可以定义转换操作符,完成从类类型到其它类型的转换。 (P455)转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型。对任何可作为函数返回类型的类型(除了void)都可以定义转换函数。不允许转换为数组或函数类型,可以转换成指针(数据和函数指针)以及引用类型。 (P455)转换函数必须是成员函数,通常定义为const成员。不能指定返回类型,且形参必须为空。但又必须显示地返回一个指定类型的值。 (P456)一个类只能定义一个类型转换。 (P464)一般而言,函数调用的候选集只包括成员函数或非成员函数,不会两者都包括。而确定操作符的使用时,操作符的非成员和成员版本可能都是候选者。 (P475)派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类的protected成员无访问权限。 (P476)一般而言,派生类只(重)定义那些与基类不同或扩展基类行为的方面。 (P477)派生类中虚函数的声明必须与基类中定义方式完全匹配。但有一个例外,返回对基类型的引用(或指针)的虚函数。派生类国的虚函数可以返回基类函数所返回类型的派生类的引用(或指针)。 (P480)引用的指针的静态类型与动态类型可以不同,这是C++用以支持多态性的基石。 (P481)只有成员函数中的代码才应该使用作用域操作符覆盖虚函数机制。 (P482)如果基类和派生类都有一个虚函数的默认实参,使用哪个实参由调用者决定,与动态类型无关。 (P484)接口继承:public继承方式,具有与基类相同的接口。实现继承:private或protected继承方式,不继承基类的接口,但使用基类的接口。 (P485)class默认private继承。Struct默认public继承 (P486)友元关系不能继承,基类的友元对派生类的成员没有特殊访问权限 (P486)如果基类定义了static成员,则整个继承层次中只有一个这样的成员。无论从基类派生出多少个派生类,每个static成员只有一个实例。 (P490)构造函数和复制控制成员不能继承,每个类定义自己的构造函数和复制控制成员。 (P491派生类构造函数的初始化列表只能初始化派生类成员,不能直接初始化继承成员。 (P492)一个类只能直接初始化直接基类 (P494)如果派生类定义了自己的复制构造函数,该复制构造函数一般应显式使用基类复制构造函数初始化对象的基类部分。派生类赋值操作符类似。派生类析构函数不同,它只负责清除自己的成员 (P497)在基类的构造函数或析构函数中,将派生类对象作为基类类型的对象来看待。因此如果在基类的构造函数或析构函数中调用虚函数,调用的是基类定义的版本。 (P498)对象、引用或指针的静态类型决定了对象能够完成的行为。 (P504)句柄类存储和管理基类指针。用户通过句柄类访问继承层次的操作。句柄的用户可以获得动态行为但无须担心指针的管理。 (P529)与调用函数模板形成对比,使用类模板时,必须为模板形参显示指定实参。