%%% 注意 %%%
- 写函数时要充分考虑到 极端 的情况
- 要注意函数中的 异常处理
- 注意 函数的返回值的类型、参数的类型
1. 将负数赋值为unsigned类型的变量在C++中是合法的。
2. float和double的区别:
- float占一个字(4字节),double占二个字(64字节)
- float只能保证6位有效数字,double型至少可以保证10为有效数字。
- double在提高了精度的同时保持了和float持平的计算代价。
3 自增(++i)、自减(--i)时避免使用后置操作符:
- 尽量使用前自增(减)操作:
- 原因:前置操作需要做的工作更少:只需加1后返回加1后的结果即可。 、
- 后置操作则必须先保存操作数原来的值,以便返回未加1之间的值作为操作的结果
- 只有在必要的时候才使用后置操作符。
- *iter++ 等价于:*(iter++) 等价于:*iter;++iter;//因为后自增操作的优先级高于解引用操作。!!!
4. C++中的 箭头(->)操作:
- 箭头(->) : 在点操作符后使用的解引用操作
- Object item;
- Object *sp = &item;
- (*sp).function(item2); // OK 对应指向对象的指针,在使用点操作之前需要对该指针进行解引用操作。
- *sp.function(item2); // Error : sp has no member named function.
- sp -> function(item2); // OK, 等价于 (*sp).function(item2);
5. cout 与 ?:输出表达式与条件操作符:
- 在输出表达式中,如果不严格使用圆括号将条件操作符括起来,将会得到意外的结果:
- cout << (i < j ? i : j); // OK 输出结果符合预期:输出 i、j 中最大的那个。
- cout << (i < j) : i : j; // OK 能正常运行,但是输出结果不符合预期:输出结果为0或1.
- 等效于:cout << (i < j); // 将根据 i、j 的大小关系输出0或1;
- cout ? i : j; // 测试cout的值,然后选择 i 或 j 。
- cout << i < j : i : j ; // Error : campares cout to int 不能通过运行。
6. sizeof操作符
- sizeof 操作符的作用是:返回一个对象或类型名的长度。
- 返回类型为: size_t 类型。
- 将 sizeof 用在 表达式 上,将获得该表达式的结果的类型长度。(但是并没有计算表达式的值)
- classA item, *p; // 下面三种操作的作用相同:求得classA的类型长度(需要持有一个该类型的对象)
- sizeof(classA); // 结果为classA
- sizeof item; // 结果为item的类型大小,即:sizeof(classA);
- sizeof *p; // 返回p指向的类型大小, 即:sizeof(classA);
- 将 sizeof 用于表达式 expr 时,并没有计算表达式 expr 的值,
- 特别是在 sizeof *p 中,指针 p 可以持有一个无效的地址,因为不需要对 p 做解引用操作,结果只跟 p 指向的对象类型有关。
- 可以使用 sizeof 操作快速计算一个数组的元素个数:
- int sz = sizeof(array ) / sizeof(*array);
7. 数组名一般作为该数组的第一个元素的指针:p156
- 不将数组转换为指针的例外情况:
- 将数组用于 取地址(&)操作符的操作数 或 sizeof 操作符的操作数时,或者用作对数组的引用进行初始化时,不会将数组转换为指针。
8. 指针:
- 指向任意数据类型的指针都可转换为 void* 类型;
- 整型数值常量 0 可转换为任意指针类型。
- 算术值和 指针值 都可以转换为 bool 类型:如果指针或算术值为0,则其bool值为false,其他值则为true。
9. 显示转换(强制类型转换)
- 强制类型转换(C++ primer P158)
- static_cast: 编译器执行的任何类型的转换都可以由static_cast显示完成。(P159)
- dynamic_cast: 支持运行时识别指针或引用所指向的对象。(P159)
- const_cast: 用来转换掉表达式的 cast 性质。(P159)
- reinterpret_cast: 为操作数的位模式提供较低层次的重新解释。(P159)
- 强制类型转换关闭或挂起了正常的类型检查,强烈建议程序员避免使用强制类型转换。
10. new 和 delete
- new 表达式返回指向新创建对象的指针,我们通过该指针访问此对象。
- int *pi = new int[20];
- delete 用来显式的将该对象占用的内存返回给自由存储区。
- delete [] pi;
- new 和 delete 应成对出现。
- 不应对不是有new申请的空间进行delete,尽管有时可以通过编译器。
- 可以对指针值为 0 的指针施加 delete 操作, 但是没有什么意义。
- 注意:
- 在 delete 之后,应重设指针的值:因为删除指针后,该指针变成悬垂指针,
- 应在 delete 指针之后,立即将该指针置 0 ,这样就非常清楚地表明指针不再指向任何对象。
11. 动态内存分配的三种常见错误:
- 删除(delete)指向动态分配内存的指针失效:因此将无法将该块内存返还给自由存储区,删除动态分配内存失败称为:“内存泄露”。
- 读写已经删除的对象:如果删除指针所指向的对象之后,将指针置为 0 值,则比较容易检测出次来错误。
- 对同一个内存空间使用两次delete表达式:
- 发生在:当两个指针指向同一个动态创建的对象时,
- 在其中一个指针上做 delete 运算时,将该对象的内存空间返还给自由存储区。
- 再第二个指针上做 delete 运算时,此时自由存储区可能会被破坏。
12. switch 内部变量的定义:p176
- 在 switch 结构中,只能在它的最后一个 case 标号 或 default 标号后面定义变量:
- case true:
- string s = f(a); // Error:由于此处定义了 s ,那么后面的 case 中都可以使用此 s ,若从后面是 case 开始执行的话,就会造成使用未定义的变量 s。
- break:
- 若一定要为某个特殊的 case 定义变量,则可以引用 块语句 ,在该块语句中定义变量,从而保证这个变量在使用前被定义和初始化。
- case true:
- {
- string s = f(a); // OK, s 的声明周期只在这个块内,不会出现使用未定义的变量的情况。
- }
- switch 中 case 标号必须是 const 常量表达式。
13. for 语句头中的多个定义:p181
- 可在 for 语句头中定义多个局部变量:
- for(int i = 0, j = 0; i < 10; i++) {} // OK,此处就在 for 的语句头定义的两个变量 i, j .
- 虽然可以定义多个对象,但是不管怎么样,该处只能出现一个语句,因此所有的对象必须具有相同的一般类型。
- for(int i = 0, string s = "string"; i < 10; i++) {} // Error: 该处楚翔多个语句,多个变量不具有相同的一般类型。
14. do while 语句:交互性 p182
- do while 相对于 while 具有较强的 交互性:
- 它能够保证循环体至少执行一次。
- 程序员可以利用该次的执行,输出给用户一些提示性信息,以介绍while循环的功能。
- 与 while 语句不同,do while 语句总是以分号结束:
- int a = 0;
- do {
- // int a = 0; // Error:不能在此处定义,在此处定义的变量将不能在while中作为条件变量。
- a++;
- } while (a != 10) ;
- 由于在 do 后的块内定义的变量只在该块内有效,则 while 中将不能使用该变量,只有将该变量定义在 do 之前,才能在 while 中使用该变量。
15. break 语句 p183
- break 语句只能出现在循环或 switch 结构中,或者出现在嵌套于循环或 switch 结构中的语句里。
- 对于 if 语句,只有当它嵌套在 switch 或循环里面时,才能使用 break 。
16. continue语句 p184
- continue 语句只能出现在 for 、 while 、 或者do while 循环中,包括嵌套在这些循环内部的块语句中。
17. C++参数传递 p199
- 每次调用函数时,都会重新创建该函数的所有的形参,此时所传递的实参将会初始化对应的形参。
- 形参的初始化与变量的初始化一样:
- 如果形参具有非引用类型,则 复制 实参的值。
- 缺点:
- 不能在函数中修改实参的值。
- 当需要以大型实参作为实参传递时,复制对象所付出的时间和存储代价比较高。
- 有些对象没法实现复制。(类类型)
- 缺点:
- 如果形参为引用类型,则它只是实参的别名。
- 在C++中 使用 引用 来传递实参比使用 指针 来传递实参更安全。
- 如果形参具有非引用类型,则 复制 实参的值。
- 指针形参:如果需要保护指针指向的值,则形参需定义为指向 const 对象的指针:
- 一方面,我们可以为 const 形参传入“const”对象实参,例const int *类型
- 另一方面,我我们也可以为 “const形参” 传入 “非const” 对象的实参。例:int *类型。
- 原因:可以将指向 “const” 对象的指针初始化为指向 “非const” 对象,但不可以让指向 “非const” 对象的指针指向 “const” 对象。
- Const 形参 (非引用)(情况和 “指针形参相反”):
- 在调用函数时,如果函数使用 “非引用(指针形参属于引用类型)的非 const 形参”:
- 既可以给该函数传递 “Const实参”。
- 也可以给该函数传递 “非 Const 实参”。
- (因为该初始化是 非引用形参 ,属于初始化复制了初始化的值,所以可以用 “Const” 对象初始化“非Const”对象,反之亦然)
- 另一原因:兼容C语言,在C语言中,具有 Const形参 或 非Const形参 的函数并无区别。
- 在调用函数时,如果函数使用 “非引用(指针形参属于引用类型)的非 const 形参”:
- Const引用(见18)
18. Const引用 与 非Const引用:p205
- 应该将不需要修改的引用形参定义为 Const引用
- 普通的 “非Const引用形参” 在使用时不太灵活,这样的形参既不能用 Const 对象初始化,也不能用字面值或产生右值的表达式实参初始化(会造成编译错误)。
- 鉴于上述原因,应谨慎使用 “非Const引用形参”。
- Const引用:
- 使用 “Const引用” 避免复制,使用引用形参都可以避免复制
- 将不修改的相应实参的形参定义为Const引用。
19. 指向指针的引用(int *&v1 ) p205
- int *&v1:的定义应从右至左理解:
- v1是一个“引用”,与指向 “int” 型对象的指针相关联,
- 也就是说,v1 是指向一个 int型 指针的引用
- v1 只是传递进 函数 的任意指针的别名(还是一个指针)
20. Vector 和其它容器类型的形参 p206
- 通常,函数不应该有 Vector或其它标准库容器类型的形参。
- 若函数使用 “普通的非引用的Vector形参”,在调用函数时,会复制 Vector 中的每一个元素(代价大,不推荐)
- 为避免复制 Vector 元素,应考虑将形参声明为引用类型。
- 但是更好的做法是:通过传递 “指向容器中需要处理的元素的迭代器” 来传递容器。
- 这样,直接对迭代器进行 “解引用” 操作,即可处理容器内的元素。
- 例:
- void print(vector<int> :: const_iterator beg, vector<int> :: const_iterator end) //当不需要改变vector内的元素时,应声明为const类型
- {
- while(beg != end)
- {
- cout << *beg++;
- if(beg != end) cout << ' ';
- }
- cout << endl;
- }
21. 数组形参 和 数组实参 p206
- 数组有两个特殊性质:
- 1. 数组不能复制---无法编写使用数组类型的形参
- 2. 使用数组名字时,数组名会自动转化为指向其第一个元素的指针---处理数组元素通常只能通过操纵指向数组中元素的指针来处理。
- 数组形参的定义方式:(以下三种定义是等价的)
- void print(int *) {} // 推荐用法,直观无歧义,一般使用 “非引用” 类型传递,当不需要修改数组元素时,应定义为 “指向Const对象的指针”
- void print(int []) {} // 虽然不能直接传递数组,但是函数的形参可以写成数组的形式。
- void prin(int [10]) {} // 编译器会忽略任何数组形参指定的长度,10 无效。
- 编译器检查数组形参关联的实参时,他只会检查实参是不是指针、指针的类型和数组元素的类型是否匹配,而不会检查数组的长度。
- 通过引用传递数组:
- 如果形参是数组的引用,编译器不会将数组实参转化为指针,而是传递数组的引用本身,这种情况下,数组的大小将会成为形参和实参类型的一部分
- 编译器在检查形参时也会检查实参的大小是否和形参的大小相同。
- 只有那些实参的长度和形参的长度相同的实参才能完成正常的调用。
- 实参的长度和形参的长度不同时,调用会出错。(这种机制另一方面体现了一种安全性)
- 例:
- void print(int (&array) [10]) // array两边的括号是必须的,因为下表操作具有更高的优先级,10也是必须的
- {
- for(size_t i = 0; i != 10; ++i)
- {
- cout << array[i] << endl;
- }
- }
- 多维数组的传递:
- 和其他数组一样,多维数组以指向 0 号元素的指针的方式传递。
- 但是由于:多维数组中的元素本身就是数组,除了第一维以为的所有维的长度都是元素类型的一部分,必须明确指定。
- 即:需要明确指定多维数组中除第一维之外的其他维的长度。
- 例:(以下两种定义方式等价)
- void print(int (matrix*)[10], int rowsize) {} // 10 必须指明, matrix为指向含有 10 个 int 型元素的数组的指针----数组指针 指向一维数组的指针,机试该指针指向的数组含有 10 个元素,但是该指针实际上指向的是 ”数组的首元素“,即该指针是指向 1 个元素的指针,而不是指向 10 个元素的指针。 因此,若遇到 int (*p) [n]时,此时的 p 一定指向多维数组。
- int a[2][10] = {};
- print(a, 2);
- void print(int matrix[] [10], int rowsize]) {} // 10 必须指明,实际上形参是一个指针,指向数组中的元素,数组中的每个元素本身就是含有10个int型对象的数组。
- int a[2][10] = {};
- print(a, 2);
- 可以使用类似vector容器的传递方法(传递一个迭代器),来传递指针的”首元素“和”末元素“的指针来访问数组。(安全,不会越界)
22. 指针数组 和 数组指针
- 数组指针 --- 指向数组的指针(本质是一个指针变量) C语言中专门用来指向二维数组的
- 定义:int (*p) [n] ; // 由于 "()" 优先级高,p是一个指针,指向一个整形的一维数组,该数组长度 n ,也是 p 的 ”步长“
- 执行 p + 1 时,p 要跨过 n 个整型数据的长度。
- 将一个二维数组赋值给一个指针:
- int a[3][4];
- int (*p)[4]; // 本语句是定义一个数组指针,指向含有 4 个元素的一维数组。
- p = a; //将该二维数组的首地址赋给 p,也就是 a[0] 或 &a[0][0]。
- p++; //该语句执行过后,也就是 p = p + 1; p 跨过行 a[0][] 指向了行 a[1][]。所以 “数组指针” 也称为 “行指针” 。p[i]不是一个元素,而是一行元素。
- 指针数组 --- 包含元素为指针的数组(本质是一个数组)
- 定义: int *p[n]; // []的优先级高,先与 p 结合成为一个数组,再由 int* 说明这是一个指针数组,数组元素为指针类型,即包含 n 个指针。
- 执行 p + 1; 操作时错误的, 执行 p = a; 操作也是错误的,因为 p 只不过是一个 “数组名字”。
- p[0]、p[1] ... ... p[n-1] 操作是允许的 ,且 p[i] 是指针变量,用来存放变量地址, *p = 5;// 改变 p[0]指向的位置的内容
- 可以将一个二维数组赋值给一个指针数组:
- int *p[3];
- int a[3][4];
- for(i = 0; i < 3; ++i)
- {
- p[i] = a[i]; // 指针数组 p 中存放着三个指针变量 p[0]、p[1]、p[2],a[i] 分别代表 a 中的第 i 行元素的首址。
- }
- 数组指针只是一个指针变量,在 C 语言里专门用来指向二维数组的,它只占一个指针的存储空间。
- 指针数组是多个指针变量,以数组的形式存在于内存当中,占有多个指针的存储空间。
- 数组指针和指针数组同时指向二维数组时,其引用和数组名引用都是一样的:
- *(p[i] + j)、*(*(p + i) + j)、(*(p + i))[j]、p[i][j]。都表示他们所指向数组的第 i 行,第 j 列的元素。
23. 引用返回左值 p215
- 返回引用的函数返回一个左值,这样的函数可用于任何要求使用左值的地方。
- 例:
- char &get_val(string &str, string :: size_type ix)
- {
- return str[ix];
- }
- int main()
- {
- string s("a value");
- cout << s << endl; // 输出“a value”
- get_val(s, 0) = 'A' ; // 改变 s 中的内容,令 s[0] = 'A'
- cout << s << endl; // 输出“A value”
- }
- 如果不希望引用的返回值被修改,返回值类型英爱声明为 Const: const char &get_val()......
24. 默认实参 p218
- C++中可以为函数提供默认实参,如果函数中有一个形参具有默认实参,那么,它后面所有的形参都必须有默认实参。
- 调用包含默认是惨的函数时,可以为该形参提供实参,也可以不提供。
- 在一个文件中,只能为一个形参指定默认实参一次。
- 通常,应在函数声明中指定默认实参,并将该声明放在合适的头文件中,在函数的定义处就不需要添加默认实参了。
- 如果放在函数的定义文件中,则只有在函数的定义文件内调用该函数,默认实参才有效。
- 例:
- 文件xxxx.h: int f(int a, int b = 10); //默认实参在这里说明
- 文件xxxx.cpp: int classA::f(int a, int b) {......} // 此处不能再添加默认实参了否则会引发编译错误。
- 例:
25. 静态局部变量 p220
- 一个变量如果位于函数的作用域内,但生命周期能够跨越这个函数的多次调用,这样的对象应定义为 static (静态的)
- static 局部变量 确保 不迟于执行流程第一次经过该对象的定义语句时进行初始化。
- 这种对象一旦被创建,在程序结束前都不会被注销。
- 当定义静态局部对象的函数结束时,静态局部对象不会被撤销。
- 在该函数的多次调用的过程中,静态局部对象会持续存在并保持它的值。
- 例:在第一次调用 count 函数之前,ctr 就已创建并赋予初值0;
- size_t count()
- {
- static size_t ctr = 0; // ctr的值在整个程序的执行结束前都有效。
- return ++ctr;
- }
- int main()
- {
- for(size_t i = 0; i != 10; ++i)
- cout << count() << endl;
- return 0;
- }
- 例:在第一次调用 count 函数之前,ctr 就已创建并赋予初值0;
26. 内联函数 p221
- 内联函数通过在函数声明时添加关键字 “inline” ,并且在函数声明中直接给出函数的定义。
- 不能将内联函数的 “声明” 和 “定义” 分开放在两个文件中,那样会造成编译错误。
- 在类的内部(声明部分)“定义” 的函数默认作为 “内联函数” 处理。
- 在 内联函数 中不能含有 递归、循环语句,同时内联函数是否会被直接展开不是绝对的,由编译器根据情况决定。
- 内联函数应该在头文件中 “定义” !
-
- 例:class A
- {
- public: // public 部分定义的成员可被使用该类型的所有代码访问
- // 内敛函数,在此处同时给出声明和定义。
- inline const string &shorterString(const string &str1, const string &str2)
- {
- return str1.size() < str2.size() ? str1 : str2 ; // 内联函数里面最好不要包括递归和循环语句。
- }
- protecter: // 在 private 部分定义的成员可被本类的其他成员访问。但该类的对象不能访问
- private: // 用于继承
- } ; //要注意 class 结尾处的 “分号(;)”
- int main()
- {
- A object;
- string a , b = "shorter", c = "long string";
- a = object.shorterString(b, c); // a 的结果为 b 和 c 中比较短的 string 即为 b 。
- }
-
27. 类的 Const 成员函数 p224
- 每一个成员函数(除了 static 成员函数外)都有一个额外的、隐含的形参 this 。--- 类似python中的函数在声明时提供this形参,调用时不提供this形参而直接使用。
- Const 成员函数该变了隐含的 this 形参的类型,使得 this 形参将是一个指向 “本类对象的Const object*类型的指针”
- Const 成员函数的结果 --- Const成员函数不能修改调用该函数的对象。
- 注意:
- Const 对象、指向 Const对象的指针或引用 只能 用于调用其 Const 成员函数。---- 对象:类的实例化
- 使用 Const对象、指向 Const 对象的指针或引用来调用 非 Const 成员函数 是错误的
- 不能在成员函数的形参表中显式地使用 this 指针,但是可以在成员函数的函数体内 显式地 使用 this 指针。
- 成员函数的声明若为 Const 成员函数,那么在函数的定义的时候 形参列表的后面也必须有 Const。
- 例:double sale(const sale &rhs) const; // 形参列表实际包含两个形参:this,const sale &rhs 。
- 类的成员函数可以访问类的 Private 成员,但是类的对象无法访问类的 Private 成员。
28. 构造函数:p225
- 合成的默认构造函数
- 一般适用于:仅包含类类型(string,vector等)的成员的类。
- 自定义默认构造函数
- 一般将构造函数定义为 Public 的,如果定义为 private 的,则不能定义(实例化并初始化)类的对象,因为类的对象无法访问类的 private 成员。
- 对于含有内置类型(int,float等)或复合类型(struct、union等)成员的类,应定义他们自己的默认构造函数初始化这些成员。
- 构造函数初始化列表:className(arg1,arg2):item1(arg1), item2(arg2) {}
29. 重载函数 p228
- 具有相同的名字而形参表不同的函数,称为重载函数。
- 如果两个函数的返回类型和形参表完全相同,则将第二个函数声明视为第一个的重复声明。
- 如果两个函数的形参表完全相同,但是返回类型 不同,则第二个声明是错误的。--- 函数不能仅仅基于不同的返回类型而实现重载。
- 重载函数都应在同一个作用域中声明(处于同一层次)----如果局部的声明一个函数,则该函数将屏蔽而不是重载在外层作用域中声明的同名函数。
- 重载和 Const 形参:
- 仅当形参是引用或指针时,形参是否为 const 才有影响,当形参是非引用或指针时,形参的初始化方式为复制,const 形参和非 Const 形参无区别。
- 可基于 “引用形参” 是指向 Const 对象还是非 Const对象 来实现重载。
- record lookup(account &)
- record lookup(const account &) // 重载
- const account a(0); account b;
- lookup(a); // 调用 lookup(const account &)
- lookup(b); // 调用 lookup(account &)
- 如果形参是普通的引用(非Const引用),则不能将 Const 对象传递给这个形参。
- 如果传递了 Const 对象,则只有带 Const 引用形参的版本才是该调用的可行函数。
- 如果传递的是“非Const对象”,则上述任意一种函数皆可行,非Const 对象既可用于初始化 Const 引用,也可用于初始化 非Const 引用。
- 但是将 Const 引用初始化为 非Const对象,需要通过转换来实现,而非 Const 形参的初始化则是精确版本。
- 但是不可基于 “指针本身是否是 Const 的” 来实现重载:
- f(int *);
- f(int *const); //二次声明而非重载
- 可基于 “指针形参” 指向的对象是 Const对象还是非 Const 对象 来实现重载。
- 当形参以副本传递时,不能基于形参是否为 Const 来实现重载。
30. 指向函数的指针 p237
- 函数的类型:由函数的返回值和形参列表确定,而与函数名无关。
- bool (*pf) (const string&, const string&); //将 pf 声明为指向函数的指针,它所指向的函数带有两个 const string & 类型的形参和 bool 类型的返回值。pf 两侧的括号是必须的
- 可使用 typedef 简化函数指针的定义:
- typedef bool (*cmpFcn) (const string&, cosnt string &); // cmpFcn 成为了一种指向函数的指针类型的名字(而不是变量名字,就像 int 一样是类型的名字)
- cmpFcn 类型为指针类型:指向返回 bool 类型并带有两个 const string 引用形参的函数的指针。
- 可以直接使用 cmpFcn 来定义这样的类型的变量:cmpFcn pf1 = 0; // 定义了一个 cmpFcn 类型的指针,该指针不指向任何函数(0)
- 若有:bool len(const sting&, const string&);
- cmpFcn pf2 = len;
- pf1 = len;
- pf2 = pf1;
- cmpFcn pf1 = len; 等价于: cmpFcn pf2 = &len;
- len("this", "is");pf1("string", "is"); (*pf1)("this", "is");
31. cout 与 cerr:
- cout的输出可以重定向到一个文件中,而cerr必须输出在显示器上。
32. 文件的输入和输出 p251
- fstream 头文件定义了三种支持文件IO的类型:(使用以下三个类型时,需要包含 fstream 头文件)
- ifstream,由 istream 派生而来,提供 “读文件” 的功能。
- ofstream,由 ostream 派生而来,提供 “写文件” 的功能。
- fstream,由 iostream 派生而来,提供 “读写同一个文件” 的功能。
- fstream 类型除了继承下来的行为外,还定义了两个自己的操作:open 和 close。
- (1)为 ifstream 或者 ofstream 对象提供文件名作为初始化式,就相当于打开了特定的文件。
- ifstream infile; // 声明一个 输入文件流
- ofstream outfile; // 声明一个输出文件流。
- infile.open("in"); // 在当前目录下打开文件 in
- outfile.open("out"); // 在当前目录下打开文件 out
- (2)IO标准库使用 C 风格字符串而不是 C++ string 类型的字符串作为文件名。
- 一般的做法:将文件名读入 string 对象,而不是 C 风格字符数组,在调用 string 对象的 c_str() 成员获取 C 风格字符串。
- (3)打开文件后,通常要检查打开是否成功,这是一个好习惯。
- if (! infile)
- {
- cerr << "error : unale to open input file."
- }
- 同时也可以通过测试对象: if (outfile) // outfile 是否可以使用 ,当返回 true 时意味着文件已经可以使用。也可以通过 if (!outfile) 处理 outfile 不能用的情况。
- (4)fstream 对象一旦打开,就保持与指定的文件相关联。
- 如果想要把 fstream 对象与另一个不同的文件关联,则必须先关闭(close)现在的文件,然后打开(open)另一个文件。
- ifstream infile("in"); // 打开名为 in 的文件
- infile.close(); // 关闭当前的文件流
- infile.clear(); // 清除流的状态
- infi.open("next"); // 打开新的名为“next”的文件---重新绑定流
- 如果想要把 fstream 对象与另一个不同的文件关联,则必须先关闭(close)现在的文件,然后打开(open)另一个文件。
- 如果程序员需要重用文件流读写多个文件,必须在读另一个文件之前调用 clear 清除该流的状态。
33. 字符串流 p257
- iostream 标准库支持内存中的输入/输出,只要将流与存储在程序内存中的 string 对象捆绑起来即可。
- 可使用 iostream 输入和输出操作符读写这个 string 对象,标准库定义了三种字符串流
- istringstream,由 istream 派生而来,提供 读 string 的功能。
- ostringstream,由 ostream 派生而来,提供 写 string 的功能。
- stringstream,由 iostream 派生而来,提供 读写 string 的功能。
- 要使用上述类,必须包含 sstream 头文件。
- 像 fstream 在 iostream 外定义了 open 和 close 一样,这些类还定义了名为 str 的成员,用来读取或设置 stringstream 对象所操纵的 string 值。
- stringstream strm; // 创建自由的 stringstream 对象。
- stringstream strm(s); // 创建存储 s 的副本的 stringstream 对象,其中 s 是 string 类型的对象。
- strm.str(); // 返回 strm 中存储的 string 类型对象。
- strm.str(s); // 将 string 类型的 s 复制给 strm ,返回 void 。
- stringstream 的作用:在多种数据类型之间实现自动格式化:
- int val1 = 512, val2 = 1024;
- ostringstream format_message;
- format_message << "val1: " << val1 << "\n" << "val2: " << val2 << "\n"; // 此时,int型自动转换为等价的可打印的字符串。
- format_message 包含的内容:val1: 512\nval2: 1024
- istringstream input_istring(format_message.str());
- string dump;
- input_istring >> dump >> val1 >> dump >> val2;
- cout << val1 << " " << val2 << endl; // prints 512 1024