整理自[传智扫地僧教学视频][1]
- 程序编译链接过程
- gcc -E main.c -o main.i 宏定义展开 (文本) “-E 只运行与编译器”
- gcc -S main.i -o main.s 生成汇编文件 (文本) “-S 产生汇编文件后停止编译”
- gcc -c main.s -o main.o 生成目标文件 (二进制) “-c 取消链接”
- gcc main.o -o app 生成可执行程序 (二进制)
泛型编程
函数模板
函数模板的本质: 让类型参数化
定义方法
template <typename T> void myswap(T &a, T &b) { T temp; temp = a; a = b; b = temp; }
调用方法
myswap<int>(a, b); // 显示调用 myswap(a, b);//自动类型推导
- 函数模板和普通函数的区别及调用规则的研究
- 函数模板要求严格的类型匹配, 不会进行自动类型转换。普通函数可以进行隐式的类型转换。
- 函数模板可以像普通函数一样进行重载。
- 在进行函数匹配时, c++编译器优先考虑普通函数。
- 如果函数模板可以产生更好的匹配,则选择函数模板。
- 可以通过空模版实参列表(<>)的语法限定编译器只通过模板匹配。
- 函数模板机制
- 编译器并不是把函数模板处理成能够处理任意类型的函数,而是通过具体类型产生不同的函数。进行了两次编译。
- 在声明的地方对模板代码进行编译
- 在调用的地方对参数替换后的代码进行编译(与调用次数无关)
- 编译器并不是把函数模板处理成能够处理任意类型的函数,而是通过具体类型产生不同的函数。进行了两次编译。
类模板
- 单个类
- 定义
- 使用
- 做函数参数 需指定具体类型
- 继承中的模板类
- 模板类派生普通类:需要具体化模板类(只能从具体的类派生)
- 模板类派生模板类
- 类模板函数(两次编译)
- 所有模板函数写在类的内部(包括友元函数)
- 所有模板函数写在类的外部
- 在一个cpp文件中
- 分开, .h 和 .cpp (hpp文件)。 main.cpp中要包含模板函数的实现文件。
- 友元函数的实现要和声明写在一个文件里
- 前置声明
- 在类中友元声明时, 函数名后加<>
- 模板类中的static
- static成员属于某一具体类型的类。而不是由该类模板生成的不同的类。
类型转换
- C 风格类型转换
type a = (type)b
- C++ 类型转换
- static_cast:
int a = static_cast<int> (b)
编译时进行类型检查 - reinterpret_cast: 不同类型间强制类型转换,重新解释。 转换指针。
- dynamic_cast: 子类和父类之间多态类型转换。向下转型,基类指针或引用安全转换为派生类的指针或引用。运行时类型识别RTTI,另一个操作符typeid。
- const_cast: 去掉const属性,即去除变量只读属性。形参为const,在函数中想修改非const实参。const实参仍不能修改(可能不报错,在函数内可以修改,但函数返回后又恢复原值)。
- static_cast:
- 要清楚要转的变量, 转换之前是何种类型,转换后是何种类型,转换的结果。一般情况下,不建议使用类型转换。
异常处理
- 基本语法
- throw; try catch
- 异常是跨函数的
- catch语句按照顺序进行检查
- 接到异常后,可以不处理,继续throw
- 异常是严格按照类型进行catch (不进行隐式类型转换)
- 栈解旋: throw时,栈变量被自动销毁
- 异常接口声明
void fun() throw(A, B, C)
只能抛出所列类型void fun()
不写, 可以抛出任何类型void fun() throw()
不抛出异常
- 异常类型 & 异常变量的生命周期
- 基础数据类型 int, char…
- 类对象类型
- 对象: throw时产生临时对象,再调用拷贝构造函数初始化catch子句中的形参(不是临时对象直接转化为形参)。
- 对象的引用: 使用throw时产生的临时对象。不掉用拷贝构造函数。(对象和引用不能同时用于catch)
- 指针: 注意避免野指针 。 为避免野指针, throw时应使用new, 在异常处理中使用delete。
- 综上,最好使用引用!将生命周期的管理交给编译器。
- 异常的层次结构(继承在异常中的应用)
标准异常库
- exception(基类)
- bad_cast
- runtime_error
- overflow_error
- underflow_error
- range_error
- logic_error
- domain_error
- invalid_argument
- out_of_range
- length_error
- bad_alloc
- virtual const char* what()
输入输出流
- 标准IO流 cin cout cerr clog
- cin类常用api
- >> 遇见空格结束
- cin.get()
- cin.getline()
- cin.ignore(N): 忽略缓冲区的N个字符。
- cin.peek(): “偷窥”
- cin.putback(): 将字符放回缓冲区
- cout类常用api
- cout.flush(): 刷新缓冲区
- cout.put(): 输出字符
- cout.write(): 输出字符串,可设置字符个数
输出控制 成员函数方式(还有控制符方式) - cout.width(): 设置输出宽度
- cout.fill(): 设置填充字符
- cout.setf(): 设置标记
- 格式化输出
- cin类常用api
- 文件IO
- 文件打开关闭
- ASCII方式读写文件
- 二进制方式读写文件
标准模板库STL
算法 容器 迭代器
- 所有容器操作都是值语意,进行拷贝操作而非引用。所以STL容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数,深拷贝)。
string类 (封装了char指针)
- 初始化 《C++ primer》 P76
- 遍历
- 数组方式 []
- at(): 与[]的区别: at()可以抛出异常
- 迭代器
- 字符指针和string的转换
- c_str()成员函数,返回指向string中字符串的指针
- copy()成员函数,拷贝string中的字符串。值拷贝N个字符,不自动添加’\0’
- 连接字符串
- append()成员函数
- operator+
- operator+=
- 查找和替换
- find(): 返回要查找的子串首字符在原字符串中的下标(从零开始)。未找到返回-1. C中的strstr()
- replace(): 从指定位置开始,删除N个字符,插入子串
- 截断和插入
- erase()
- insert()
- 大小写转换
- transform()
容器
顺序容器: vector; deque; list;stack; 每个元素有固定的位置。取决于插入时机,与元素值无关
- array 固定大小数组
- vector
- 动态数组,尾部添加或移除元素快,在中间和头部操作耗时。
- 获取修改元素常用函数 返回引用,可当左值
- at()
- operator[]
- front()
- back()
- 插入删除
- insert()
- push_back()
- erase()
- pop_back()
- clear()
- 遍历
- 数组方式
- 迭代器 迭代器失效问题
- end()返回的迭代器指向的是最后一个元素之后的位置!
- rend()返回的是指向首元素之前的位置
- vector对象是如何增长的
在必须分配新空间时,会分配比实际需求更大的内存空间作为预留,而不是每次插入都进行分配。
- deque
- 双端数组,可以在尾部和头部快速插入和删除。
- 额外支持的操作
- push_front()
- pop_front()
- stack
- top()
- push()
- pop()
- size()
- empty()
- queue
- push()
- pop() 移除队首元素
- back()
- front()
- size
- empty
- forward_list 单向链表
- list
- 双向链表容器,可以高效的插入和删除
- 不支持随机存取,因此不支持at()和operator[]。 迭代器可以递增、递减,但不支持加数字
- 插入删除
- push_front()
- pop_front()
- push_back()
- pop_back()
- insert()
- erase()
- remove()成员函数
- priority_queue 优先级队列 堆结构
- 最大值(默认)优先级队列
privority_queue<int>
或privority_queue<int, vector<int>, less<int> >
- 最小值优先队列
privority_queue<int, vector<int>, greater<int> >
- 最大值(默认)优先级队列
关联容器: set; multiset; map; multimap; 元素位置取决于特定的排序准则,与插入顺序无关。
- set 有序集合,所包含元素唯一。红黑树
- 集合中的元素按一定顺序排列,元素插入过程是按排序规则插入,所以不能指定插入位置。默认情况从小到大。
set<int>
或set<int, less<int> >
set<int, greater<int> >
- 插入操作和删除操作比vector快。
- 不可以直接存取元素。 不支持at()和operator[]
- 自定义数据类型排序
- 仿函数/函数对象: 定义一个类(class、struct),类中重载函数调用操作符
set<int, Fun>
- 仿函数/函数对象: 定义一个类(class、struct),类中重载函数调用操作符
- pair 对组
- insert()函数返回值 pair
- first
- second
- set的查找
- count()
- find()
- equal_range()
- lower_bound()
- upper_bound()
- 集合中的元素按一定顺序排列,元素插入过程是按排序规则插入,所以不能指定插入位置。默认情况从小到大。
- multiset
- 与set相比,同一个值可以出现多次
map 键值对(key, value)
插入元素
map<int, string> m; m.insert({1, "abc"});//方法一 m.insert(pair<int, string> (1, "abc")); //方法二 m.insert(make_pair(2, "def"));//方法三 m.insert(map(int, string>::value_type(3, "ghi"));//方法四 m[4] = "gkl";//方法五 //前四种重复插入会失败 //第五种若map中有key = 4, 其内容被覆盖
查找
- find()
multimap
- 与map相比,相同键可以出现多次。multimap不支持[]操作符。
- 应用场景: 数据分组
STL算法
- 函数对象: 重载了函数调用操作符的类,其对象成为函数对象。
- 函数对象()的执行,很像函数调用, 因此又称仿函数
- 函数对象与普通函数的区别
- 函数对象是属于类对象,突破函数的概念,能保持调用状态信息。
- 函数对象可当返回值。
- 一元函数对象 函数参数1个
- 二元函数对象 函数参数2个
- 一元谓词
- 函数参数1个
- 函数返回值是bool型,可以作为一个判断式
- 谓词可以是仿函数,也可以是回掉函数
- 二元谓词
- 函数参数两个
- 返回类型是bool型
- 预定义函数对象
- 函数适配器
- 绑定适配器
- bind1st() 预定义函数对象和第一个参数绑定
- bind2nd() 预定义函数对象和第二个参数绑定
- 组合适配器
- 指针函数适配器
- 成员函数适配器
- 绑定适配器
- 理论总结
- STL通过类模板技术,实现了数据类型和容器模型的分离
- STL的迭代器技术实现了遍历容器的统一方法。
- STL的算法,通过函数对象实现了自定义数据类型的算法运算。
常用算法
遇到问题追踪源码
遍历算法
- for_each()
- 通过指定函数(函数对象或回掉函数)依次对一组迭代器范围内的元素进行访问, 可进行修改
- 返回值是函数对象
- 一般情况下,for_each()所使用的函数对象,参数是引用,没有返回值
transform()
两种形式
transform(b1, e1, b2, op) 若b2 = b1, 同for_each
transform(b1, e1, b2, b3, op)replace() 如果想以某值替换符合规则的元素,使用replace()更快
- 一般情况下,transform()所使用的函数对象,参数一般不使用引用,而且函数有返回值。
- for_each()
- 查找算法
- adjacent_find() 查找一对相邻的重复元素, 找到则返回这对元素的第一个迭代器
- binary_search() 在有序(二分法)序列中查找值,找到返回true.
- count() 与给定值相等的元素个数 (基础数据类型)
- count_if() 自定义数据类型
- find() 基础数据类型
- find_if() 自定义数据类型
- equal_range()
- 排序
- merge(): 合并两个有序序列, 存放到另一个序列
- sort()
- random_shuffle(): 随机洗牌
- reverse()
- 拷贝和替换
- copy()
- replace()
replace(v.begin(), v.end(), 3, 8)
将所有的3换位8 - replace_if() 使用谓词,符合某一条件的变成新值
- swap()
- 算术
- accumulate() 对指定范围内的元素求和,再加上一个指定的初始值
- 生成
- fill(): 将值赋值给指定范围内的所有元素
- 集合算法 并不是只用于set类型
- set_union() 并集
- set_intersection() 交集
- set_difference() 差集