函数模板与类模板:{ '将类型定义为参数 实现代码重用机制的工具'
函数模板:{
建立一个通用函数 其函数返回类型和形参类型不具体指定 用一个虚拟的类型来代表 具体形式如下: {
template <typename 类型参数> // 或 template <class 类型参数> 返回类型 函数名(模板形参表) { 函数体 }
}
#include <iostream> using namespace std; template <typename T> T Max(T *arry, int numb = 0) { T max = arry[0]; for(int i = 1; i < numb; i++) { if(arry[i] > max) { max = arry[i]; } } return max; } int main() { int arry[] = {1, 2, 9, 4, 5}; cout << "The max number is: " << Max(arry, 5) << endl; double arry1[] = {1.1, 4.2, 3.3}; cout << "The max number is: " << Max(arry1, 3) << endl; return 0; }
'template' -- 声明模板的关键字 '类型参数' -- 常用T 、type等 使用函数模板时 必须将类型实例化
类型参数前需加关键字'typename或class' 表示一个虚拟的类型名
#include <iostream> using namespace std; template <class T> T Max(T a, T b) { return (a > b)? a : b; } template <class T> T MAX(T a, T b, T c) { T max = a > b? a : b; max = max > c? max : c; return max; } int main() { cout << "The two number max is: " << Max(3, 5) << endl; cout << "The three number max is: " << MAX(9, 5, 7) << endl; return 0; }
在函数模板中允许使用多个类型参数 template定义部分的每个类型参数前必须有关键字'typename或class'
在template语句与函数模板定义语句之间不允许插入别的语句
函数模板与同名的非模板函数也可以重载
调用的顺序是:首先寻找一个参数完全匹配的非模板函数 如果找到了就调用它
若没有找到 则寻找函数模板 将其实例化 产生一个匹配的模板参数 若找到了 就调用它
}
类模板:{
建立一个通用类 其数据成员 成员函数的返回类型和形参类型不具体指定 用一个虚拟的类型来代表
使用类模板定义对象时 系统会根据实参的类型来取代类模板中虚拟类型 从而实现不同类的功能
eg:{
template <typename T> class Classname{ private: T data1, data2, data3; public: Classname(T a, T b, T c) : data1(a), data2(b), data3(c) {} T sum() { return data1 + data2 + data3; } };
}
上面的例子 成员函数是定义在类体内的 类模板中的成员函数也可以在类模板体外定义 若成员函数中有类型参数存在 则C++有一些特殊的规定:
需要在成员函数定义之前进行模板声明
在成员函数名前要加上 ' 类名<类型参数>:: '
在类模板体外定义的成员函数的一般形式如下 : {
template <typename T> 函数类型 类名<类型参数>::成员函数名(形参表) { 函数体 }
则上述例子中的sum函数可以改写如下:
template <typename T> T Classname<T>::sum() { return data1 + data2 + data3; }
}
}
}
C++的输入和输出:{ '以字节流形式实现'
优点: 完全支持c语言的输入输出系统 建立面向对象的输入输出系统 比c更安全可靠
可重载 << >> 运算符 实现用户自定义类型的输入输出 书写形式简单清晰 可读性强
C++的流库及其基本结构: {
'流' 指数据从一个源流到一个目的的抽象 从流中提取数据称为输入操作(提取操作) 向流中添加数据称为输出操作(插入操作)
输入: 字节流从输入设备流向内存 输出: 字节流从内存流向输出设备
文件和字符串也可以看成有序的字节流 分别称为文件流和字符串流
常用的输入输出头文件:{
'iostream' 包含输入/输出流进行操作所需的基本信息
'fstream' 用于用户管理文件的I/O操作 (针对磁盘文件的操作)
'strstream' 用于字符串流的I/O操作 (针对内存字符串空间的I/O操作)
'iomanip' 用于输入/输出的格式控制
}
用流定义的对象称为流对象 以下是几个预定义的流对象:
'cin' 标准输入流对象 从标准输入设备读取数据
'cout' 标准输出流对象 向标准输出设备输出数据
'cerr' 标准错误流对象 向标准错误设备输出错误信息
'clog' 缓冲型标准错误流对象 向缓冲区输出错误信息
使用istream和类ostream流对象的一些成员函数 实现字符的输出和输入: {
'put()函数' cout.put(单字符/字符形变量/ASCII码);
'get()函数' cin.get(字符型变量) get()函数在读入数据时可包括空白符 提取运算符'>>'在默认情况下拒绝接受空白符
'getline()函数' cin.getline(字符数组, 字符个数n, 终止标志字符) cin.getline(字符指针, 字符个数n, 终止标志字符)
'ignore()函数' cin.ignore(n, 终止字符) ignore()函数的功能是跳过输入流中n个字符(默认个数1) 或在遇到指定的终止字符(默认终止字符是EOF)时提前结束
}
预定义类型输入/输出的格式控制:{
流成员函数进行输入/输出格式控制 {
'setf()函数' 用于设置流的状态标志 如setf(ios::scientific)
'unsetf()函数' 用于清除流的状态标志
'width()函数' 用于设置域宽
'precision()函数' 用于设置实数的精度
'fill()函数' 用于设置填充字符
}
}
使用预定义的操作符进行输入/输出格式控制
使用用户自定义的操作符进行输入/输出格式控制 {
若输出流定义操作符函数 则形式如下
ostream &操作符名(ostream &stream)
{
自定义代码
return stream;
}
若为输入流定义操作符函数 形式如下
istream &操作符名(istream &stream)
{
自定义代码
return stream;
}
}
#include <iostream> #include <iomanip> using namespace std; ostream &output(ostream &stream) { stream.setf(ios::left); //设置输出左对齐 stream << setw(10) << hex << setfill('-'); //将输出 设置域宽为10 十六进制 填充字符为- return stream; } int main() { cout << 123 << endl; cout << output << 123 << endl; return 0; }
}
文件的输入/输出:{ '文件' --存放在外部介质上的数据的集合
'文件流' -- 以外存文件为输入/输出对象的数据流 输出文件流是从内存流向外存文件的数据 输入文件流是从外存流向内存的数据
根据文件中数据的组织形式 文件分为两类: 文本文件和二进制文件
进行文件操作的一般步骤如下: {
定义一个流对象 -- 建立文件 -- 进行读写操作 -- 关闭文件
三个文件流类:
istream -- 输入文件流类
ofstream -- 输出文件流类
fstream -- 输入/输出文件流类
执行文件输入/输出的一般步骤如下:{
含头文件fstream
建立流对象
使用open()函数打开文件
进行读写操作
使用close()函数关闭文件
}
}
#include <iostream> #include <fstream> using namespace std; int main() { ofstream fout("./test.txt", ios::out); if (!fout) { cout << "Cannot open output file." << endl; exit(1); } fout << "I am a student."; fout.close(); ifstream fin("./test.txt", ios::in); if (!fin) { cout << "Cannot open input file." << endl; exit(1); } char str[80]; fin.getline(str, 80); cout << str << endl; fin.close(); return 0; }
文件打开方式: {
ios::in -- 以读方式打开文件
ios::out -- 以写方式打开文件
ios::ate -- 打开文件时指针指向文件末尾
ios::app -- 以追加方式打开文件
ios::trunc -- 若文件已存在 则清空文件内容
ios::binary -- 以二进制方式打开文件
}
二进制文件读写
用read()函数和write()函数读写二进制文件 格式如下:
inf.read(char *buf, int len);
outf.write(const char* buf, int len );
#include <iostream> #include <fstream> using namespace std; int cput() { ofstream outf("test.txt", ios::out); if(!outf) { cout << "Cannot open output file." << endl; exit(1); } char ch = 'a'; for(int i = 0; i< 26; i++) { outf.put(ch); ch++; } outf.close(); return 0; } int cget() { ifstream inf("test.txt", ios::in); if(!inf) { cout << "Cannot open input file." << endl; exit(1); } char ch; while(inf.get(ch)) { cout << ch << ' '; } cout << endl; inf.close(); return 0; } int main() { cput(); cget(); return 0; }
检测文件结束:
在文件结束的地方有一个标志位 'EOF' 采用文件流方式读取文件时 使用eof()函数 可检测到该结束符
若该函数返回'非零值' 则到达文件末尾 否则未到文件末尾
eg:{
ifstream ifs; ... if(!ifs.eof()) { cout << "文件未到末尾" << endl; }
还有一个检测方法就是检查该流对象是否为零 为零表示文件结束
ifstream ifs; ··· if (!ifs){ cout << "文件结束" << endl; } ···
检测文件流对象的某些成员函数的返回值是否为0 为0表示该流到达了末尾
while (cin.get(ch))
cut.put(ch);
}
#include <iostream> #include <fstream> using namespace std; struct strList { char course[15]; int score; }; int main() { strList obj[2] = {{"C++", 90}, {"Java", 85}}; ofstream outf("test.txt", ios::binary); //以二进制格式打开文件 if(!outf) { cout << "Cannot open output file." << endl; // exit(1); abort(); } for(int i = 0; i< 2; i++) { outf.write((char*) &obj[i], sizeof(obj[i])); } outf.close(); ifstream inf("test.txt", ios::binary); if(!inf) { cout << "Cannot open input file." << endl; abort(); } for(int i = 0; i< 2; i++) { inf.read((char*) &obj[i], sizeof(obj[i])); cout << "Course: " << obj[i].course << " Score: " << obj[i].score << endl; } inf.close(); return 0; }
键盘上输入字符时 其结束符是Ctrl+Z 按下【Ctrl+Z】组合键 eof()函数返回的值为真
is_open() 函数:
用于检测文件流对象是否已打开 若已打开 返回true 否则返回false
eg:{
ifstream ifs("test.txt"); if(ifs.is_open()){ cout << "文件已打开" << endl;
}
fail() 函数:
用于检测文件流对象的状态 若文件流对象发生错误(failbit or badbit被置为) 则返回true 否则返回false
failbit -- 发生非致命错误 一般为软件错误 可挽回 badbit -- 发生致命错误 一般为硬件或系统底层错误 不可挽回
eg:{
ifstream ifs("test.txt"); if(ifs.fail()){ cout << "文件打开失败" << endl; }
}
bad()函数:
用于检测文件流对象的状态 若文件流对象发生致命错误(badbit被置为) 则返回true 否则返回false
eg:{
ifstream ifs("test.txt"); if(ifs.bad()){ cout << "文件流发生致命错误" << endl; }
}
good()函数:
用于检测文件流对象的状态 若文件流对象处于正常状态() 则返回true 否则返回false
eg:{
ifstream ifs("test.txt"); if(ifs.good()){ cout << "文件流处于正常状态" << endl; }
}
clear()函数:
用于清除文件流对象的状态 若文件流对象发生错误 则调用clear()函数 则文件流对象的状态恢复正常
eg:{
ifstream ifs("test.txt"); if(ifs.fail()){ ifs.clear(); cout << "文件流发生错误" << endl; }
}
}
}
异常处理与命名空间:{
异常处理:{
常见错误: 编译时错误 主要是语法错误
运行时错误 统称为异常
如果在执行一个函数的过程中出现异常 可以不在本函数中立即处理 而是发出一个信息 传给它的上一级处理(即调用函数)
若上一级函数也不能处理 则逐级上传 若到最高一级仍无法处理 则运行系统将自动调用系统函数'terminate()' 由它调用'abort()'终止程序
catch(捕获异常) 、throw(抛出异常)、try(异常处理块)
eg:{
try{ string msg("This is a test exception"); throw msg; }catch(const string &e){ printf("exception = %s",e.c_str()); }
}
}
命名空间:{
namespace(命名空间)
自行命名的内存区域 将各命名空间中声明的标识符与该命名空间标识符建立关联 保证不同命名空间的同名标识符不发生冲突
eg: {
namespace myspace{ int x = 10; } int main(){ myspace::x = 20; return 0; }
}
}
c++使用不带扩展名的头文件: 如 #inlcude <cstring>
#include <iostream> #include <cmath> using namespace std; double triangle(double a, double b, double c) { double s = (a + b + c)/2; if(a+b <= c || a+c <= b || b+c <= a) { throw 1.0; //抛出double异常 } return sqrt(s * (s-a) * (s-b) * (s-c)); } int main() { double zozoA, zozoB, zozoC; try{ cout << "Enter three sides of a triangle: " << endl; cin >> zozoA >> zozoB >> zozoC; if(zozoA <= 0 || zozoB <= 0 || zozoC <= 0) { throw 1; //抛出int异常 } if(zozoA >0 && zozoB >0 && zozoC >0){ cout << "The area of the triangle is: " << triangle(zozoA, zozoB, zozoC) << endl; } }catch(double){ cout << "Invalid input, please enter positive numbers." << endl; } catch(int){ cout << "边长小于0" << endl; } return 0; }
}
STL标准模板库 {
STL(Standard Template Library) -- 标准模板库 是一个模板集合 包含了常用的数据结构和算法
'vector' 容器:{
与数组相似 包含一组地址连续的存储单元 可进行查询、插入、删除等操作
头文件: #include <vector>
vector<类型> 变量名;
eg: vector<int> vec; // 声明一个int类型的vector
vector<vector<string>> file; //该向量的元素是vector对象
vector对象初始化:
vector<T> vec1; // 默认初始化 元素类型为T
vector<T> vec2(vec1); // 拷贝初始化 等价于 vec2 = vec1
vector<T> vec3(n, val); // 元素个数为n 值均为val 类型为T
vector<T> vec4(n); // 元素个数为n 默认值 类型为T
vector<T> vec5{a,b,c,d,...}; // 初始化多个元素 等价于vec5 = {a,b,c,d,...}
'push_back()函数' 向vector中添加元素
eg:{
vector<int> vec;
vec.push_back(10); // 向vec中添加元素10
}
注释: vector下标可用于访问已存在的元素 但不能用于添加元素 当vector是空时 只能用push_back()添加或访问元素
#include <iostream> #include <vector> using namespace std; int main() { vector<int> zoVec; zoVec.insert(zoVec.begin(), 99); zoVec.insert(zoVec.begin(), 88); zoVec.insert(zoVec.end(), 77); zoVec.push_back(695); cout << "当前zoVec中的元素为: " << endl; for(int i = 0; i < zoVec.size(); i++){ cout << zoVec[i] << " "; } cout << endl; cout << zoVec.at(2) << endl; zoVec.erase(zoVec.begin()); zoVec.pop_back(); cout << "现在zoVec中的元素为: " << endl; for(int i = 0; i < zoVec.size(); i++){ cout << zoVec[i] << " "; } cout << endl; return 0; }
}
'list' 容器:{
头文件: #include <list>
list<类型> 变量名;
eg: list<int> lst; // 声明一个元素类型为int的list
#include <iostream> #include <list> using namespace std; int main() { list<int> zoList; list<int>::iterator niter; // 定义迭代器 指向int类型元素 zoList.push_back(123); zoList.push_back(456); zoList.push_back(789); cout << "链表内容 : " << endl; for(niter = zoList.begin(); niter != zoList.end(); niter++) cout << *niter << " "; cout << endl; zoList.reverse(); // 反转链表 cout << "反转链表内容 : " << endl; for(niter = zoList.begin(); niter != zoList.end(); niter++) cout << *niter << " "; cout << endl; zoList.reverse(); return 0; }
}
'stack' :{
头文件: #include <stack>
stack<类型> 变量名;
eg: stack<int> stk; // 声明一个元素类型为int的stack
#include <iostream> #include <stack> using namespace std; int main() { stack<int> zoSt; int zoNum = 100; cout << "100的八进制为: "; while(zoNum) { zoSt.push(zoNum % 8); zoNum /= 8; } int zoS; while(!zoSt.empty()) { zoS = zoSt.top(); cout << zoS; zoSt.pop(); } cout << endl; return 0; }
}
'queue' :{
头文件: #include <queue>
queue<类型> 变量名;
eg: queue<int> que; // 声明一个元素类型为int的queue
#include <iostream> #include <queue> using namespace std; int main() { queue<int> zoQue; for(int i = 0; i<10; i++) zoQue.push(i*3 + i); while(!zoQue.empty()){ cout << zoQue.front() << " "; zoQue.pop(); } cout << endl; return 0; }
}
'priority_queue' 优先队列:{
头文件: #include <queue>
priority_queue<类型> 变量名;
eg: priority_queue<int> pq; // 声明一个元素类型为int的优先队列
#include <iostream> #include <queue> #include <functional> #include <cstdlib> #include <ctime> using namespace std; int main() { priority_queue<int> zoPq; srand((unsigned)time(0)); for(int i=0; i < 6; i++) { int zoR = rand(); cout << zoR << " "; zoPq.push(zoR); } cout << endl; cout << "优先队列中的元素: " << endl; for(int i=0; i < 6; i++) { cout << zoPq.top() << " "; zoPq.pop(); } return 0; }
}
'deque' 双端队列:{
push_back();
push_front();
insert();
pop_back();
pop_front();
erase();
begin();
end();
rbegin();
rend();
size();
maxsize();
}
'set' :{
头文件: #include <set>
set<类型> 变量名;
eg: set<int> st; // 声明一个元素类型为int的set
#include <iostream> #include <set> #include <string> using namespace std; int main() { set<string> zoSet; zoSet.insert("my"); zoSet.insert("hair"); zoSet.insert("wet"); if(zoSet.count("my") != 0) { cout << "Existential element : my " << endl; } set<string>::iterator zoIter; for(zoIter = zoSet.begin(); zoIter != zoSet.end(); zoIter++) { cout << *zoIter << " "; } cout << endl; return 0; }
}
'map' :{
头文件: #include <map>
map<类型1,类型2> 变量名;
eg: map<string,int> mp; // 声明一个元素类型为string,值类型为int的map
#include <iostream> #include <map> #include <string> using namespace std; int main() { map<string, int> zoMap; zoMap["apple"] = 100; zoMap["banana"] = 200; zoMap["orange"] = 300; if(zoMap.count("apple")) { cout << "The price of apple is: " << zoMap.at("apple") << endl; // cout << "The price of apple is: " << zoMap["apple"] << endl; } return 0; }
}
}
08-18
267
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
08-16
122
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
08-17
261
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)