1.7 文件的读写
#include <fstream>
ifstream ifile("input.txt");
ofstream ofile("output.txt", ios_base::app);//追加模式
fstream iotile("test.txt", ios_base::in|ios_base::app);
iofile.seekg(0);//定位至起始处
2.1 如何撰写函数
思想:
- 对函数传入的参数做“是否合理”的判断;
- 如果不合理,可采用终止程序操作
exit()
通常需要带参数,这里填-1; - 最好的方法是采用异常处理:不过第七章才开始讨论。
2.2 调用一个函数
引用:
- 声明的时候就要初始化;
- 后期不可修改(本身就是另一对象的引用,无法修改);
- 引用的对象必须是可修改的(左值);
- 除非定义为常量引用。
指针和引用作为函数参数的区别:
- 指针在使用前必须判断是否为空;
- 引用具体指向某个对象,所以不需要;
- 指针可以设置默认值0(表示并未指向任何对象);
- 引用不能设为0(原来以为引用不能设置默认值,后来看见
ostream &os = cout
,说明也可以)。
2.4 使用局部静态对象
为了解决每次调用函数,重复计算的问题,使用局部静态对象。(为了解决函数间的通信问题而将对象定义于file scope内,永远都是一种冒险,还会打乱不同函数间的独立性,使他们难以理解)
const vector<int>* fibon_seq(int size)
{
static vector<int> elems;
return &elems;
}
此方法对应:1、引用类型函数无法返回局部变量;2、指针类型函数返回局部变量后会不可预估。所以现在返回局部静态变量则是可行的,因为他的内存不会释放。
2.5 inline函数
一个大函数拆分成几个小函数,调用多个函数有可能影响执行效能。
C++提供了一个解决办法,将这些函数声明为inline,调用的地方在编译时会以一份函数码副本取而代之。
有一定要求:体积小、常被调用、计算并不复杂(递归就不行)。
2.6 重载函数
2.7 模板函数
如果函数具备多种实现方式,我们可将它重载。
如果重载函数间只是参数的数据类型不同,可引入模板函数。
模板函数同时也可以是重载函数。
// 模板函数再经重载
template <typename T> void display(const string &msg, const vector<T> &vec);
template <typename T> void display(const string &msg, const list<T> &lst);
2.8 函数指针
为了使bool fibon_elem(int pos, int &elem);
函数获得更好的通用性(调用任一个计算数列的函数),而不是仅仅调用fibon_seq(pos)
。
引入函数指针作为参数。bool seq_elem(int pos, int &elem, const vector<int>* (*seq_ptr)(int));
更为复杂的函数指针数组的定义:const vector<int>* (*seq_array[])(int) = {fibon_seq, lucas_seq, ...);
为此将6个求数列的函数都放在了一个数组中,方便调用。
但另一个问题又产生了,怎么找到想要函数的位置呢?这里引入枚举类:enum:enum ns_type {ns_fibon, ns_lucas, ...};
练习2.5、2.6
标准库中求数组中的最大元素值。
#include <algorithm>
vector<T> vec;
T *max_element(vec.begin(), vec.end());
T a[];
T *max_element(a, a + size);// 数组的地址,数组大小
3 泛型编程风格
STL主要由两种组件构成:容器(container)、泛型算法(generic algorithm)。
容器分为:序列式容器(sequential container)(vector、list)、关联式容器(associative container)(map、set)。
3.1 指针的算术运算
本节讲解实现stl中find函数,从初级版不断扩充其通用性。
- 传入
vector<int> vec
参数,利用循环到vec.size()
,逐一查找; - 支持int 和其它类型(前提是有相等运算符):利用template 传入
vector<T> vec
,循环到vec.size()