C++编写软件(推荐):Visual Studio
Visual Studio安装:
1 C++初识 1.1 创建新项目 选择Visual C++下的空项目,然后给项目取一个名;右键源文件,添加->新建项,在弹出的页面中选择C++文件(.cpp),给它取一个名称.
1.2 注释 作用:方便程序员阅读
俩种格式: 1.单行注释: //描述信息
通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明 2.多行注释: /* 描述信息 */
通常放在一段代码的上方,对该段代码做整体说明 1.3 变量 作用: 给一段指定的内存空间起名,方便操作这段内存
语法: 数据类型 变量名 = 初始值
1.4 常量 作用: 用于记录程序中不可更改的数据
C++定义常量的俩种方式: 1.#define宏常量: #define 常量名 常量值
通常在文件上方定义,表示一个常量 2.const修饰的变量: const 数据类型 常量名 = 常量值
通常在变量定义前加关键字const,修饰该变量为常量,不可修改 1 2 3 #define month 12 const int month = 12 ;
1.5 关键字 作用: 定义变量或者常量时,不能使用关键字
C++关键字如下:
asm do if return typedef auto double inline short typeid bool dynamic_cast int signed typename break else long sizeof union case enum mutable static unsigned catch explicit namespace static_cast using char export new struct virtual class extern operator switch void const false private template volatile const_cast float protected this wchar_t continue for public throw while default friend register true delete goto reinterpret_cast try
1.6 标识符命名规则 标识符不能是关键字 标识符只能由字母、数字、下划线组成 第一个字符必须为字母或下划线 标识符中字母区分大小写 标识符争取做到见名知意的效果
2 数据类型 2.1 整型 数据类型 占用空间 取值范围 short(短整型) 2字节 -2^15~2^15-1 int(整型) 4字节 -2^31~2^31-1 long(长整型) Window为4字节,Linux为4字节(32位),8字节(64位) -2^31~2^31-1 long long(长长整型) 8字节 -2^63~2^63-1
2.2 sizeof关键字 作用: 利用sizeof关键字可以统计数据类型所占内存大小
语法: sizeof(数据类型/变量)
1 2 3 4 cout << "short占用内存空间为:" << sizeof (short ) << endl; cout << "int占用内存空间为:" << sizeof (int ) << endl; cout << "long占用内存空间为:" << sizeof (long ) << endl; cout << "long long占用内存空间为:" << sizeof (long long ) << endl;
大小比较:
short < int <= long <= long long
2.3 实型(浮点型) 作用: 用于表示小数
浮点型变量分为俩种: 1.单精度float 2.双精度double
数据类型 占用空间 有效数字范围 float 4字节 7位有效数字 double 8字节 15~16位有效数字
1 2 3 4 5 float f1 = 3.14f ;double d1 = 3.14 ;float f2 = 3e2 ;float f3 = 3e-2 ;
默认情况下输出一个小数会显示出6位有效数字
2.4 字符型 作用: 字符型变量用于显示单个字符
语法: char ch = 'a';
注意1: 在显示字符型变量时,用单引号将字符括起来,不要用双引号
注意2: 单引号内只能有一个字符,不可能是字符串
C和C++中字符型变量只占用1个字节 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码(int(ch))放入到存储单元 a - 97 A - 65
2.5 转义字符 作用: 用于表示一些不能显示出来的ASCII字符 常用转义字符有: \n \\ \t
转义字符 含义 ASCII码值(十进制) \a 警报 007 \b 退格(BS),将当前位置移到前一列 008 \f 换页(FF),将当前位置移到下页开头 012 \n 换行(LF),将当前位置移到下一行开头 010 \r 回车(CR),将当前位置移到本行开头 013 \t 水平制表(HT) (跳到下一个TAB位置) 009 \v 垂直制表(VT) 011 \ 代表一个反斜线字符"" 092 ’ 代表一个单引号(撇号)字符 039 " 代表一个双引号字符 034 \? 代表一个问号 063 \0 数字0 000 \ddd 8进制转义字符,d范围0~7 3位8进制 \xhh 16进制转义字符,h范围0~9,A~F 3位16进制
2.6 字符串型 作用: 用于表示一串字符
俩种风格: 1.C风格字符串: char 变量名[] = "字符串值";
1 2 char str1[] = "hello world" ;
2.C++风格字符串: string 变量名 = "字符串值";
1 2 string str2 = "hello world" ;
2.7 布尔类型bool 作用: 布尔数据类型代表真或假的值
bool类型只有俩个值:
true — 真 (本质是1) false — 假 (本质是0) bool类型占1个字节大小
2.8 数据的输入 作用: 用于从键盘获取数据
关键字: cin
语法: cin >> 变量
bool类型只要是非0的值都代表真
3 运算符 作用: 用于执行代码的运算
运算符类型 作用 算术运算符 用于处理四则运算 赋值运算符 用于将表达式的值赋值给变量 比较运算符 用于表达式的比较,并返回一个真值或假值 逻辑运算符 用于根据表达式的值返回真值或假值
3.1 算术运算符 作用: 用于处理四则运算
运算符 术语 示例 结果 + 正号 +3 3 - 负号 -3 -3 + 加 1+2 3 - 减 2-1 1 * 乘 1*2 2 / 除 2/1 2 % 取模(取余) 10%3 1 ++ 前置递增 a=2;b=++a; a=3;b=3; ++ 后置递增 a=2;b=a++; a=3;b=2; – 前置递减 a=2;b=–a; a=1;b=1; – 后置递减 a=2;b=a–; a=1;b=2;
俩个整数相除结果依然是整数,将小数部分去除
前置递增 先让变量+1,然后进行表达式计算;后置递增 先进行表达式计算,然后让变量+1(递减同理)
3.2 赋值运算符 作用: 用于将表达式的值赋值给变量
运算符 术语 示例 结果 = 赋值 a=2;b=3; a=2;b=3; += 加等于 a=0;a+=2; a=2; -= 减等于 a=5;a-=3; a=2; *= 乘等于 a=2;a*=2; a=4; /= 除等于 a=4;a/=2; a=2; %= 模等于 a=3;a%=2; a=1;
3.3 比较运算符 作用: 用于表达式的比较,并返回一个真值或假值
运算符 术语 示例 结果 == 相等于 4==3 0 != 不等于 4!=3 1 < 小于 4<3 0 > 大于 4>3 1 <= 小于等于 4<=3 0 >= 大于等于 4>=3 1
3.4 逻辑运算符 作用: 用于根据表达式的值返回真值或假值
运算符 术语 示例 结果 ! 非 !a 如果a为假,则!a为真;如果a为真,则!a为假。 && 与 a&&b 如果a和b都为真,则结果为真,否则为假。 || 或 a||b 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。
在C++中,除0外都为真
4 程序流程结构 C/C++支持最基础的三种程序运算结构:顺序结构、选择结构、循环结构
顺序结构:程序按顺序执行,不发生跳转 选择结构:依据条件是否满足,有选择的执行相应功能 循环结构:依据条件是否满足,循环多次执行某段代码 4.1 选择结构 4.1.1 if语句 作用: 执行满足条件的语句
if语句的三种形式
1.单行格式if语句: if(条件){条件满足执行的语句}
2.多行格式if语句: if(条件){条件满足执行的语句}else{条件不满足执行的语句}
3.多条件的if语句: if(条件1){条件1满足执行的语句}else if(条件2){条件2满足执行的语句}... else{条件都不满足执行的语句}
嵌套if语句: 在if语句中,可以嵌套使用if语句,达到更精确的条件判断
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> using namespace std;int main () { int score = 0 ; cout << "请输入你的分数:" << endl; cin >> score; cout << "你的分数为:" << score << endl; if (score > 600 ) { cout << "恭喜你考上一本" << endl; if (score > 700 ) { cout << "恭喜你考上北大" << endl; } else if (score>650 ) { cout << "恭喜你考上清华" << endl; } else { cout << "恭喜你考上重大" << endl; } } else if (score>500 ) { cout << "恭喜你考上二本" << endl; } else if (score>400 ) { cout << "恭喜你考上三本" << endl; }else { cout << "未考上本科大学" << endl; } system ("pause" ); return 0 ; }
4.1.2 三目运算符 作用: 通过三目运算符实现简单的判断
语法: 表达式1?表达式2:表达式3
解释:
如果表达式1的值为真,执行表达式2,并返回表达式2的结果; 如果表达式1的值为假,执行表达式3,并返回表达式3的结果。 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> using namespace std;int main () { int a = 10 ; int b = 20 ; int c = 0 ; c = (a > b ? a : b); system ("pause" ); return 0 ; }
4.1.3 switch语句 作用: 执行多条件分支语句
语法:
1 2 3 4 5 6 7 switch (表达式){ case 结果1 : 执行语句1 ;break ; case 结果2 : 执行语句2 ;break ; ... default : 执行语句;break ; }
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> using namespace std;int main () { int score = 0 ; cout << "请打分" << endl; cin >> score; cout << "你打的分为" << score << endl; switch (score) { case 10 : cout << "经典" << endl; break ; case 9 : cout << "经典" << endl; break ; case 8 : cout << "很好" << endl; break ; case 7 : cout << "很好" << endl; break ; case 6 : cout << "一般" << endl; break ; case 5 : cout << "一般" << endl; break ; default : cout << "不好" << endl; break ; } system ("pause" ); return 0 ; }
case里如果没有break,那么程序会一直向下执行
if与switch区别? switch 缺点:判断时只能是整数或者字符,不可以是一个区间 switch 优点:结构清晰,执行效率高
4.2 循环结构 4.2.1 while循环语句 作用: 满足循环条件,执行循环语句
语法: while(循环条件){循环语句}
解释: 只要循环条件的结果为真,就执行循环语句
写循环时应避免死循环!!!
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> using namespace std;int main1 () { int num = 0 ; while (num < 10 ) { cout << num << endl; num++; } system ("pause" ); return 0 ; }
4.2.2 do…while循环语句 作用: 满足循环条件,执行循环语句
语法: do{循环语句}while(循环条件);
注意: 与while的区别在于do…while会先执行一次循环语句,在判断循环条件
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> using namespace std;int main () { int num = 0 ; do { cout << num << endl; num++; } while (num<10 ); system ("pause" ); return 0 ; }
生成随机数 添加随机数种子 利用系统当前时间生成随机数,防止每次随机数相同 srand((unsigned int)time(NULL)); int num = rand() % 100 + 1; 生成 0 + 1 ~ 99 + 1随机数
案例: 水仙花数 案例描述: 水仙花数是指一个3位数,它的每个位上的数字的3次幂之和等于它本身 例如: 1^3+5^3+3^3=153
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> using namespace std;int main () { int num = 100 ; do { int a = num % 10 ; int b = num / 10 % 10 ; int c = num / 100 ; if (a*a*a+b*b*b+c*c*c == num) { cout << num << endl; } num++; } while (num<1000 ); system ("pause" ); return 0 ; }
4.2.3 for循环语句 作用: 满足循环条件,执行循环语句
语法: for(起始表达式;条件表达式;末尾循环体){循环语句;}
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> using namespace std;int main () { for (int i = 0 ;i < 10 ;i++) { cout << i << endl; } system ("pause" ); return 0 ; }
4.2.4 嵌套循环 作用: 在循环体中再嵌套一层循环,解决一些设计问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std;int main () { for (int i = 1 ;i <= 9 ;i++) { for (int j = 1 ;j <= i;j++) { cout << j << "x" << i << "=" << (i * j)<<"\t" ; } cout << endl; } system ("pause" ); return 0 ; }
4.3 跳转语句 4.3.1 break语句 作用: 用于跳出选择结构或循环结构
break 使用场景: 1.switch语句中 case:break; 2.循环语句中 跳出当前循环 3.嵌套循环语句中 跳出内部循环
4.3.2 continue语句 作用: 在循环语句中,跳出本次循环中余下尚未执行的语句,继续执行下一次循环
continue:筛选条件符合的内容
4.3.3 goto语句 作用: 可以无条件跳转语句
语法: goto 标记;
翻译: 如果标记的名称存在,在执行到goto语句时,会跳转到标记的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> using namespace std;int main () { cout << "1 ..." << endl; cout << "2 ..." << endl; goto FLAG; cout << "3 ..." << endl; cout << "4 ..." << endl; FLAG: cout << "5 ..." << endl; system ("pause" ); return 0 ; }
运行结果:
5 数组 5.1 概述 所谓数组就是一个集合里面存放了相同类型的数据结构
特点1:数组中的每个数据元素都是相同的数据类型 特点2:数组是由连续的内存位置组成的
5.2 一维数组 5.2.1 一维数组定义方式 1.数据类型 数组名[数组长度];
2.数据类型 数组名[数组长度] = {值1,值2...};
3.数据类型 数组名[] = {值1,值2...};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std;int main () { int arr1[5 ]; arr1[0 ] = 10 ; arr1[1 ] = 20 ; arr1[2 ] = 30 ; arr1[3 ] =40 ; arr1[4 ] = 50 ; int arr2[5 ]={10 ,20 ,30 ,40 ,50 }; int arr3[]={10 ,20 ,30 ,40 ,50 }; system ("pause" ); return 0 ; }
数组元素逆置 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <iostream> using namespace std;int main () { int arr[] = { 1 ,2 ,3 ,5 ,7 }; cout << "逆序前" << endl; for (int i = 0 ; i < sizeof (arr)/sizeof (arr[0 ]); i++) { cout << arr[i] << endl; } int start = 0 ; int end = sizeof (arr) / sizeof (arr[0 ]) - 1 ; while (start<end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } cout << "逆序后" << endl; for (int j = 0 ; j < sizeof (arr) / sizeof (arr[0 ]); j++) { cout << arr[j] << endl; } system ("pause" ); return 0 ; }
运行结果:
冒泡排序 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> using namespace std;int main () { int arr[9 ] = { 4 ,2 ,8 ,0 ,5 ,7 ,1 ,3 ,9 }; cout << "排序前" << endl; for (int i = 0 ; i < sizeof (arr) / sizeof (arr[0 ]); i++) { cout << arr[i] << endl; } for (int i = 0 ; i < sizeof (arr) / sizeof (arr[0 ]) - 1 ; i++) { for (int j = 0 ; j < sizeof (arr) / sizeof (arr[0 ])-i - 1 ; j++) { if (arr[j] > arr[j + 1 ]) { int temp = arr[j]; arr[j] = arr[j + 1 ]; arr[j + 1 ] = temp; } } } cout << "排序后" << endl; for (int j = 0 ; j < sizeof (arr) / sizeof (arr[0 ]); j++) { cout << arr[j] << endl; } system ("pause" ); return 0 ; }
运行结果:
5.3 二维数组 5.3.1 二维数组定义方式 1.数据类型 数组名[行数][列数];
2.数据类型 数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};
3.数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};
3.数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4};;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> using namespace std;int main () { int arr[2 ][3 ] = { {1 ,2 ,3 }, {4 ,5 ,6 } }; system ("pause" ); return 0 ; }
6 函数 作用: 将一段经常用的代码封装起来,减少重复代码
6.1 函数的定义 函数的定义一般主要有5个步骤
1、返回值类型 2、函数名 3、参数列表(形参) 4、函数体语句 5、return表达式
语法:
1 2 3 4 5 6 返回值类型 函数名 (参数列表) { 函数体语句 return 表达式 }
6.2 函数的调用 功能: 使用定义好的函数
语法: 函数名 (参数)
当调用函数时候,实参的值会传递给形参
6.3 值传递 所谓值传递,就是函数调用时实参将数值传入给形参 值传递时,如果形参发生,并不会影响实参 6.4 函数的常见样式 1.无参无返 2.有参无返 3.无参有返 4.有参有返
6.5 函数的声明 作用: 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义
6.6 函数的分文件编写 作用: 让代码结构更加清晰
函数分文件编写一般有4个步骤: 1.创建.h头文件 头文件右键->添加->新建项,在弹出页面中选择头文件(eg:max.h) 2.创建.cpp源文件 源文件右键->添加->新建项,在弹出页面中选择源文件(eg:max.cpp) 3.头文件写函数声明 4.源文件写函数定义
代码:
max.h
1 2 3 4 #include <iostream> using namespace std;int max (int a, int b) ;
max.cpp
1 2 3 4 5 int max (int a, int b) { return a > b ? a : b; }
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 #include <iostream> using namespace std;#include "max.h" int main () { int a = 10 ; int b = 20 ; int c = max (a, b); cout << c << endl; system ("pause" ); return 0 ; }
7 指针 作用: 可以通过指针间接访问内存
内存编号是从0开始记录的,一般用十六进制数字表示 可以利用指针变量保存地址 7.1 指针变量的定义和使用 语法: 数据类型 * 变量名;
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> using namespace std;int main () { int a = 10 ; int * p = &a; cout << "a地址为" << &a << endl; cout << "p=" << p << endl; system ("pause" ); return 0 ; }
7.2 空指针和野指针 空指针: 1.用于指针变量初始化: int* p =NULL; 2.空指针不可以进行访问:0 ~ 255之间的内存编号是系统占用
野指针:避免出现 int* p= (int *)0x1100;
7.3 const修饰指针 const修饰指针:常量指针const int* p = &a(指针指向可以修改,指针指向的值不可以改) 指针常量int * const p = &a(指针指向不可以改,指针指向的值可以修改) const int* const p = &a(指针指向,指针指向的值都不可以改)
7.4 指针和数组 作用: 利用指针访问数组中元素
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> using namespace std;int main () { int arr[10 ] = { 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,0 }; int * p = arr; cout << "arr[0]=" << *p<<endl; p++; cout << "arr[1]=" << *p << endl; cout << "指针遍历数组" << endl; int * p2 = arr; for (int i = 0 ; i < 10 ; i++) { cout << *p2 << endl; p2++; } system ("pause" ); return 0 ; }
7.5 指针和函数 作用: 利用指针作函数参数,可以修改实参的值
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std;void swap (int * p1, int * p2) ;int main () { int num1 = 10 ; int num2 = 20 ; swap (&num1, &num2); cout << "num1=" << num1 << endl; cout << "num2=" << num2 << endl; system ("pause" ); return 0 ; } void swap (int * p1, int * p2) { int temp = *p1; *p1 = *p2; *p2 = temp; }
运行结果:
案例: 指针、数组、函数 案例描述: 封装一个函数,利用冒泡排序,实现对整型数组的升序排序 例如数组: int arr[10] = {4,3,6,9,1,2,10,8,7,5};
代码:
bubbleSort.h
1 2 3 4 #include <iostream> using namespace std;void bubbleSort (int * arr, int len) ;void printArray (int * arr, int len) ;
bubbleSort.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <iostream> using namespace std;void bubbleSort (int * arr, int len) { for (int i = 0 ; i < len - 1 ; i++) { for (int j = 0 ; j < len- i - 1 ; j++) { if (arr[j] > arr[j + 1 ]) { int temp = arr[j]; arr[j] = arr[j + 1 ]; arr[j + 1 ] = temp; } } } } void printArray (int * arr, int len) { for (int i = 0 ; i < len; i++) { cout << arr[i] << endl; } }
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> using namespace std;#include "bubbleSort.h" int main () { int arr[10 ] = {4 ,3 ,6 ,9 ,1 ,2 ,10 ,8 ,7 ,5 }; int len = sizeof (arr) / sizeof (arr[0 ]); bubbleSort (arr, len); printArray (arr, len); system ("pause" ); return 0 ; }
运行结果:
8 结构体 结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
8.1 结构体定义和使用 语法: struct 结构体名 {结构体成员列表};
通过结构体创建变量的方式有三种:
struct 结构体名 变量名 struct 结构体名 变量名 = {成员1值,成员2值…} 定义结构体时顺便创建变量 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std;#include <string> struct Student { string name; int age; int score; }s3; int main () { struct Student s1; s1. name = "张三" ; s1. age = 18 ; s1. score = 80 ; struct Student s2= { "李四" ,19 ,88 }; s3. name = "王五" ; s3. age = 17 ; s3. score = 90 ; system ("pause" ); return 0 ; }
8.2 结构体数组 作用: 将自定义的结构体放入到数组中方便维护
语法: struct 结构体名 数组名[元素个数] = {{},{},...{}}
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> using namespace std;#include <string> struct Student { string name; int age; int score; }; int main () { struct Student stuArray[3 ] = { {"张三" ,18 ,100 }, {"李四" ,16 ,89 }, {"王五" ,18 ,98 } }; stuArray[2 ].name = "张六" ; stuArray[2 ].age = 19 ; stuArray[2 ].score = 90 ; for (int i = 0 ; i < 3 ; i++) { cout << "姓名:" << stuArray[i].name << " 年龄:" << stuArray[i].age << " 分数:" << stuArray[i].score << endl; } system ("pause" ); return 0 ; }
8.3 结构体指针 作用: 通过指针访问结构体中的成员
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std;#include <string> struct Student { string name; int age; int score; }; int main () { Student s = { "张三" ,18 ,90 }; Student* p = &s; cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl; system ("pause" ); return 0 ; }
8.4 结构体嵌套结构体 作用: 结构体中的成员可以是另一个结构体
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> using namespace std;#include <string> struct Student { string name; int age; int score; }; struct Teacher { int id; string name; int age; struct Student stu; }; int main () { Teacher t; t.id = 10 ; t.name = "老王" ; t.age = 50 ; t.stu.name = "小王" ; t.stu.age = 18 ; t.stu.score = 100 ; cout << "老师姓名:" << t.name << " 老师年龄:" << t.age << "学生姓名:" << t.stu.name << " 学生年龄:" << t.stu.age << " 学生分数:" << t.stu.score << endl; system ("pause" ); return 0 ; }
8.5 结构体做函数参数 作用: 将结构体作为参数向函数中传递
传递方式有俩种:
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> using namespace std;struct student { string name; int age; int score; }; void printStudent1 (struct student s) { cout << "值传递子函数 : 姓名:" << s.name << endl; cout << "值传递子函数 : 年龄:" << s.age << endl; cout << "值传递子函数 : 分数:" << s.score << endl; } void printStudent2 (struct student *p) { cout << "地址传递子函数 : 姓名:" << p->name << endl; cout << "地址传递子函数 : 年龄:" << p->age << endl; cout << "地址传递子函数 : 分数:" << p->score << endl; } int main () { struct student s; s.name = "张三" ; s.age = 20 ; s.score = 85 ; cout << "main : 姓名:" << s.name << endl; cout << "main : 年龄:" << s.age<< endl; cout << "main : 分数:" << s.score << endl; printStudent1 (s); printStudent2 (&s); system ("pause" ); return 0 ; }
运行结果:
总结:如果不想修改主函数中的数据用值传递,反之用地址传递
8.6 结构体中const使用场景 作用: 用const来防止误操作
案例: 结构体 案例描述: 设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。通过冒泡排序算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果
五名英雄信息如下:
1 2 3 4 5 {"刘备",23,"男"}, {"关羽",22,"男"}, {"张飞",20,"男"}, {"赵云",21,"男"}, {"貂蝉",19,"女"},
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <iostream> using namespace std;#include <string> struct Hero { string name; int age; string sex; }; void bubbleSort (struct Hero heroArray[], int length) { for (int i = 0 ; i < length-1 ; i++) { for (int j = 0 ; j < length-i-1 ; j++) { if (heroArray[j].age > heroArray[j + 1 ].age) { struct Hero temp = heroArray[j]; heroArray[j] = heroArray[j + 1 ]; heroArray[j + 1 ] = temp; } } } } void printHero (struct Hero heroArray[], int length) { for (int i = 0 ; i < length; i++) { cout << "姓名:" << heroArray[i].name << " 年龄:" << heroArray[i].age << " 性别:" << heroArray[i].sex << endl; } } int main () { struct Hero heroArray[5 ] = { {"刘备" ,23 ,"男" }, {"关羽" ,22 ,"男" }, {"张飞" ,20 ,"男" }, {"赵云" ,21 ,"男" }, {"貂蝉" ,19 ,"女" } }; int length = sizeof (heroArray) / sizeof (heroArray[0 ]); cout << "排序前:" << endl; printHero (heroArray, length); bubbleSort (heroArray, length); cout << "排序后:" << endl; printHero (heroArray, length); system ("pause" ); return 0 ; }
运行结果:
通讯录管理系统 功能要求:
菜单功能:用户选择功能的界面 void showMenu() 添加联系人:向通讯录中添加新人,信息包括(姓名、性别、年龄、联系电话、家庭地址)最多记录1000人 步骤:设计联系人结构体 设计通讯录结构体 main函数中创建通讯录 封装添加联系人函数 测试添加联系人功能 显示联系人:显示通讯录中所有联系人的信息 步骤: 删除联系人:按照姓名进行删除指定联系人 步骤:封装检测联系人是否存在 封装删除联系人函数 测试删除指定联系人 查找联系人:按照姓名查看指定联系人信息 步骤: 修改联系人:按照姓名重新修改指定联系人 步骤: 清空联系人:清空通讯录中所有信息 退出通讯录:退出当前使用的通讯录 当用户选择0时,退出 提示代码:system("cls");
//清屏
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 #include <iostream> using namespace std;#include <string> #define MAX 1000 struct Person { string m_Name; int m_Sex; int m_Age; string m_Phone; string m_Addr; }; struct Addressbooks { struct Person personArray[MAX]; int m_Size; }; void addPerson (Addressbooks* abs) { if (abs->m_Size==MAX) { cout << "通讯录已满,无法添加" << endl; return ; } else { string name; cout << "请输入姓名:" << endl; cin >> name; abs->personArray[abs->m_Size].m_Name = name; cout << "请输入性别:" << endl; cout << "1 --- 男" << endl; cout << "2 --- 女" << endl; int sex = 0 ; while (true ) { cin >> sex; if (sex==1 ||sex==2 ) { abs->personArray[abs->m_Size].m_Sex = sex; break ; } cout << "输入有误,重新输入" << endl; } cout << "请输入年龄:" << endl; int age = 0 ; while (true ) { cin >> age; if (age > 0 && age<150 ) { abs->personArray[abs->m_Size].m_Age = age; break ; } cout << "输入有误,重新输入" << endl; } cout << "请输入联系电话:" << endl; string phone; cin >> phone; abs->personArray[abs->m_Size].m_Phone = phone; cout << "请输入家庭住址:" << endl; string address; cin >> address; abs->personArray[abs->m_Size].m_Addr = address; abs->m_Size++; cout << "添加成功" << endl; system ("pause" ); system ("cls" ); } } void showPerson (Addressbooks* abs) { if (abs->m_Size == 0 ) { cout << "当前记录为空" << endl; } else { for (int i = 0 ; i < abs->m_Size; i++) { cout << "姓名:" << abs->personArray[i].m_Name << "\t" ; cout << "性别:" << (abs->personArray[i].m_Sex==1 ?"男" :"女" )<< "\t" ; cout << "年龄:" << abs->personArray[i].m_Age << "\t" ; cout << "电话:" << abs->personArray[i].m_Phone<< "\t" ; cout << "地址:" << abs->personArray[i].m_Addr << endl; } } system ("pause" ); system ("cls" ); } int isExist (Addressbooks* abs, string name) { for (int i = 0 ; i < abs->m_Size; i++) { if (abs->personArray[i].m_Name == name) { return i; } } return -1 ; } void deletePerson (Addressbooks* abs) { cout << "请输入删除的联系人" << endl; string name; cin >> name; int ret = isExist (abs, name); if ( ret!= -1 ) { for (int i = ret; i < abs->m_Size; i++) { abs->personArray[i] = abs->personArray[i + 1 ]; } abs->m_Size--; cout << "删除成功" << endl; } else { cout << "查无此人" << endl; } system ("pause" ); system ("cls" ); } void findPerson (Addressbooks *abs) { cout << "请输入要查找的联系人" << endl; string name; cin >> name; int ret=isExist (abs, name); if (ret != -1 ) { cout << "姓名:" << abs->personArray[ret].m_Name << "\t" ; cout << "性别:" << (abs->personArray[ret].m_Sex == 1 ? "男" : "女" ) << "\t" ; cout << "年龄:" << abs->personArray[ret].m_Age << "\t" ; cout << "电话:" << abs->personArray[ret].m_Phone << "\t" ; cout << "地址:" << abs->personArray[ret].m_Addr << endl; } else { cout << "查无此人" << endl; } system ("pause" ); system ("cls" ); } void modifyPerson (Addressbooks* abs) { cout << "请输入要修改的联系人" << endl; string name; cin >> name; int ret = isExist (abs, name); if (ret != -1 ) { string name; cout << "请输入姓名:" << endl; cin >> name; abs->personArray[ret].m_Name = name; cout << "请输入性别:" << endl; cout << "1 --- 男" << endl; cout << "2 --- 女" << endl; int sex = 0 ; while (true ) { cin >> sex; if (sex == 1 || sex == 2 ) { abs->personArray[ret].m_Sex = sex; break ; } cout << "输入有误,重新输入" << endl; } cout << "请输入年龄:" << endl; int age = 0 ; while (true ) { cin >> age; if (age > 0 && age < 150 ) { abs->personArray[ret].m_Age = age; break ; } cout << "输入有误,重新输入" << endl; } cout << "请输入联系电话:" << endl; string phone; cin >> phone; abs->personArray[ret].m_Phone = phone; cout << "请输入家庭住址:" << endl; string address; cin >> address; abs->personArray[ret].m_Addr = address; } else { cout << "查无此人" << endl; } system ("pause" ); system ("cls" ); } void clearPerson (Addressbooks* abs) { abs->m_Size = 0 ; cout << "通讯录已清空" << endl; system ("pause" ); system ("cls" ); } void showMenu () { cout << "************************" << endl; cout << "***** 1.添加联系人 *****" << endl; cout << "***** 2.显示联系人 *****" << endl; cout << "***** 3.删除联系人 *****" << endl; cout << "***** 4.查找联系人 *****" << endl; cout << "***** 5.修改联系人 *****" << endl; cout << "***** 6.清空联系人 *****" << endl; cout << "***** 0.退出通讯录 *****" << endl; cout << "************************" << endl; } int main () { Addressbooks abs; abs.m_Size = 0 ; int select = 0 ; while (true ) { showMenu (); cin >> select; switch (select) { case 1 : addPerson (&abs); break ; case 2 : showPerson (&abs); break ; case 3 : deletePerson (&abs); break ; case 4 : findPerson (&abs); break ; case 5 : modifyPerson (&abs); break ; case 6 : clearPerson (&abs); break ; case 0 : cout << "欢迎下次使用!" << endl; system ("pause" ); return 0 ; break ; default : cout << "请重新输入" << endl; break ; } } system ("pause" ); return 0 ; }
2 C++核心编程 2.1 内存分区模式 代码区:存放函数体的二进制代码,由操作系统进行管理的 特点:共享、只读 全局区:存放全局变量和静态变量以及常量 该区域的数据在程序结束后由操作系统释放 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等 不要返回局部变量的地址:第一次可以正确打印是因为编译器做了保留,第二次这个数据就不再保留了 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收 主要利用new在堆区开辟内存 程序运行前:代码区、全局区
不在全局区:局部变量,const修饰局部变量(局部常量) 全局区:全局变量,静态变量(static关键字修饰 eg:static int s_a = 10),常量:字符串常量、const修饰全局变量
程序运行后:栈区、堆区
内存4区意义: 不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程
指针本质是局部变量
2.1.1 new操作符 C++中利用new
操作符在堆区开辟数据 堆区开辟的数据,由程序员手动开辟、手动释放,释放利用操作符delete
语法: new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针 释放数组delete[] arr;
2.2 引用 2.2.1 引用的基本使用 作用: 给变量起别名
语法: 数据类型 &别名 = 原名
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> using namespace std;int main () { int a = 10 ; int & b = a; cout << "a=" << a << endl; cout << "b=" << b << endl; int c = 100 ; b = c; cout << "a=" << a << endl; cout << "b=" << b << endl; }
2.2.2 引用做函数参数 作用: 函数传参时,可以利用引用的技术让形参修饰实参 优点: 简化指针修改实参
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> using namespace std;int main () { int a = 10 ; int b = 20 ; mySwap (a,b); cout << "a=" << a << endl; cout << "b=" << b << endl; } void mySwap (int &a,int &b) { int temp=a; a=b; b=temp; }
2.2.3 引用做函数返回值 作用: 引用是可以作为函数的返回值存在的
1.不能返回局部变量的引用 2.函数的调用可以作为左值
本质:指针常量 int * const ref = &a
2.2.4 常量引用 作用: 常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参
,防止形参改变实参
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std;void showValue (const int & val) ;int main () { int a = 100 ; showValue (a); system ("pause" ); return 0 ; } void showValue (const int & val) { cout << "val=" << val << endl; }
2.3 函数(提升) 2.3.1 函数默认参数 在C++中,函数的形参列表中的形参是可以有默认值的
语法: 返回值类型 函数名 (参数 = 默认值){}
注意: 1.如果某个位置已经有了默认参数,那么从这个位置往后,从左往右都必须有默认值 2.如果函数声明有默认参数,函数实现就不能有默认参数(声明和实现只能有一个默认参数)
2.3.2 函数占位参数 语法: 返回值类型 函数名 (数据类型){}
占位参数也可以有默认参数(void func(int a,int = 10))
2.3.3 函数重载 作用: 函数名可以相同,提高复用性
条件: 1.同一作用域 2.函数名称相同 3.函数参数类型不同 ,或者个数不同 ,或者顺序不同
函数返回值不可以作为函数重载的条件
引用可作为重载条件:void func(int& a)(传入变量时执行)与 void func(const int& a)(传入数据时执行) 函数重载遇到函数默认参数 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> using namespace std;void func () { cout << "func()的调用" << endl; } void func (int a) { cout << "func(int a)的调用" << endl; } void func (double a) { cout << "func(double a)的调用" << endl; } void func (int a,double b) { cout << "func(int a,double b)的调用" << endl; } void func (double a,int b) { cout << "func(double a,int b)的调用" << endl; } int main () { func (3.14 ,10 ); system ("pause" ); return 0 ; }
2.4 类和对象 C++面向对象三大特性为:封装、继承、多态
C++认为万事万物皆为对象
,对象上有其属性和行为
具有相同性质的对象可以抽象称为类
2.4.1 封装 封装的意义:
将属性和行为作为一个整体,表现生活中的事物 将属性和行为加以权限控制 封装意义一: 在设计类的时候,属性和行为写在一起,表现事物
语法: class 类名{ 访问权限: 属性 / 行为 };
类中的属性和行为 我们统一称为 成员 属性: 成员属性 成员变量 行为: 成员函数 成员方法
封装意义二: 类在设计时,可以把属性和行为放在不同的权限下,加以控制 访问权限有三种:
1.public 公共权限 成员 类内可以访问 类外可以访问 2.protected保护权限 成员 类内可以访问 类外不可以访问 儿子可以访问父亲中的保护内容 3.private 私有权限 成员 类内可以访问 类外不可以访问 儿子不可以访问父亲中的私有内容
struct和class区别:
struct默认权限为公共 class默认权限为私有 成员属性设置为私有: 优点: 1.将所有成员属性设置为私有,可以自己控制读写权限 set写;get读 2.对于写权限,我们可以检测数据的有效性
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <iostream> using namespace std;#include <string> class Student { public : string m_Name; int m_Id; void showStudent () { cout << "姓名:" << m_Name << ";学号:" << m_Id << endl; } void setName (string name) { m_Name = name; } void setId (int id) { m_Id = id; } protected : string m_Car; private : string m_Password; public : void func () { m_Name = "张三" ; m_Car = "bike" ; m_Password = "123456" ; } };
案例: 设计立方体类 案例描述:
设计立方体类(Cube) 求出立方体的面积和体积 分别用全局函数和成员函数判断俩个立方体是否相等
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include <iostream> using namespace std;class Cube { public : void setL (int l) { m_L = l; } int getL () { return m_L; } void setW (int w) { m_W = w; } int getW () { return m_W; } void setH (int h) { m_H = h; } int getH () { return m_H; } int calculateS () { return 2 * m_L*m_W + 2 * m_W*m_H + 2 * m_H*m_L; } int calculateV () { return m_L * m_W * m_H; } bool isSameByClass (Cube &c) { if (m_L == c.getL () && m_W == c.getW () && m_H == c.getH ()) { return true ; } return false ; } private : int m_L; int m_W; int m_H; }; bool isSame (Cube &c1, Cube &c2) { if (c1. getL ()==c2. getL ()&&c1. getW ()==c2. getW ()&&c1. getH ()==c2. getH ()) { return true ; } return false ; } int main () { Cube c1; c1. setL (10 ); c1. setH (10 ); c1. setW (10 ); cout << "面积为:" << c1. calculateS () << endl; cout << "体积为:" << c1. calculateV () << endl; Cube c2; c2. setL (10 ); c2. setH (10 ); c2. setW (10 ); cout << "全局函数判断" << endl; bool ret = isSame (c1, c2); if (ret) { cout << "c1与c2相等" << endl; } else { cout << "c1与c2不相等" << endl; } cout << "成员函数判断" << endl; ret = c1. isSameByClass (c2); if (ret) { cout << "c1与c2相等" << endl; } else { cout << "c1与c2不相等" << endl; } system ("pause" ); return 0 ; }
案例: 点和圆的关系 案例描述: 设计一个圆形类(Circle)和一个点类(Point),计算点和圆的关系
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 #include <iostream> using namespace std;class Point { public : void setX (int x) { m_X = x; } int getX () { return m_X; } void setY (int y) { m_Y = y; } int getY () { return m_Y; } private : int m_X; int m_Y; }; class Circle { public : void setR (int r) { m_R = r; } int getR () { return m_R; } void setCenter (Point center) { m_Center=center; } Point getCenter () { return m_Center; } private : int m_R; Point m_Center; }; void isInCircle (Circle& c, Point& p) { int distance = (c.getCenter ().getX () - p.getX ()) * (c.getCenter ().getX () - p.getX ()) + (c.getCenter ().getY () - p.getY ()) * (c.getCenter ().getY () - p.getY ()); int rDistance = c.getR () * c.getR (); if (distance==rDistance) { cout << "点在圆上" << endl; } else if (distance>rDistance) { cout << "点在圆外" << endl; } else { cout << "点在圆内" << endl; } } int main () { Circle c; c.setR (10 ); Point center; center.setX (10 ); center.setY (0 ); c.setCenter (center); Point p; p.setX (10 ); p.setY (10 ); isInCircle (c, p); system ("pause" ); return 0 ; }
分文件:
point.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #pragma once #include <iostream> using namespace std;class Point { public : void setX (int x) ; int getX () ; void setY (int y) ; int getY () ; private : int m_X; int m_Y; };
point.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "point.h" void Point::setX (int x) { m_X = x; } int Point::getX () { return m_X; } void Point::setY (int y) { m_Y = y; } int Point::getY () { return m_Y; }
circle.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #pragma once #include <iostream> using namespace std;#include "point.h" class Circle { public : void setR (int r) ; int getR () ; void setCenter (Point center) ; Point getCenter () ; private : int m_R; Point m_Center; };
circle.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include "circle.h" void Circle::setR (int r) { m_R = r; } int Circle::getR () { return m_R; } void Circle::setCenter (Point center) { m_Center = center; } Point Circle::getCenter () { return m_Center; }
Main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> using namespace std;#include "point.h" #include "circle.h" void isInCircle (Circle& c, Point& p) { int distance = (c.getCenter ().getX () - p.getX ()) * (c.getCenter ().getX () - p.getX ()) + (c.getCenter ().getY () - p.getY ()) * (c.getCenter ().getY () - p.getY ()); int rDistance = c.getR () * c.getR (); if (distance==rDistance) { cout << "点在圆上" << endl; } else if (distance>rDistance) { cout << "点在圆外" << endl; } else { cout << "点在圆内" << endl; } } int main () { Circle c; c.setR (10 ); Point center; center.setX (10 ); center.setY (0 ); c.setCenter (center); Point p; p.setX (10 ); p.setY (10 ); isInCircle (c, p); system ("pause" ); return 0 ; }
2.4.2 对象的初始化和清理 构造函数和析构函数:解决对象的初始化和清理问题
如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。
构造函数语法: 类名(){}
1.构造函数无返回值 不用写void 2.函数名与类名相同 3.可以有参数,可以发生重载 4.创建对象时会自动调用构造函数且只调用一次
析构函数: ~类名(){}
1.无返回值 不用写void 2.函数名与类名相同 在名称前加~
3.不可以有参数,不可以发生重载 4.对象在销毁前会自动调用,且只调用一次
构造函数的分类及调用 1.分类
按参数分:有参构造和无参构造 按类型分:普通构造和拷贝构造 2.三种调用方式 括号法 显示法 隐式转换法 拷贝构造函数调用时机
使用一个已经创建完毕的对象来初始化一个新对象 值传递的方式给函数参数传值 以值方式返回局部对象 构造函数调用规则 默认情况下,C++编译器至少给一个类添加3个函数 1.默认构造函数(无参,函数体为空) 2.默认析构函数(无参,函数体为空) 3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
如果用户定义有参构造函数,C++不在提供默认无参构造,但是会提供默认拷贝构造 如果用户定义拷贝构造函数,C++不会再提供其他构造函数 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <iostream> using namespace std;class Person { public : Person () { cout << "无参构造函数调用" << endl; } Person (int a) { cout << "有参构造函数调用" << endl; } ~Person () { cout << "析构函数调用" << endl; } Person (const Person& p) { cout << "拷贝构造函数调用" << endl; } }; void test01 () { Person p1; Person p2=Person (10 ); Person p3=Person (p2); Person (10 ); Person p4 = 10 ; Person p5 = p4; } int main () { Person p; system ("pause" ); return 0 ; }
深拷贝和浅拷贝: 浅拷贝:简单的赋值拷贝操作 深拷贝:在堆区重新申请空间,进行拷贝操作
浅拷贝:问题:堆区内存重复释放 深拷贝:m_Height=new int(*p.m_Height);
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <iostream> using namespace std;class Person { public : Person () { cout << "Person默认构造函数调用" << endl; } Person (int age,int height) { m_Age = age; m_Height = new int (height); cout << "Person有参构造函数调用" << endl; } Person (const Person& p) { cout << "拷贝构造函数的调用" << endl; m_Age = p.m_Age; m_Height = new int (*p.m_Height); } ~Person () { if (m_Height != NULL ) { delete m_Height; m_Height = NULL ; } cout << "Person析构函数调用" << endl; } int m_Age; int * m_Height; }; void test01 () { Person p1 (18 ,160 ) ; cout << "p1的年龄:" << p1. m_Age <<"身高:" <<*p1. m_Height<< endl; Person p2 (p1) ; cout << "p2的年龄:" << p2. m_Age << "身高:" << *p2. m_Height << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
初始化列表:
作用: C++提供了初始化列表语法,用来初始化属性
语法: 构造函数():属性1(值1),属性2(值2)...{}
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> using namespace std;class Person { public : Person (int a,int b,int c) :m_A (a), m_B (b), m_C (c) { } int m_A; int m_B; int m_C; }; void test01 () { Person p (30 , 20 , 10 ) ; cout << "m_A=" << p.m_A << endl; cout << "m_B=" << p.m_B << endl; cout << "m_C=" << p.m_C << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
类对象作为类成员:
C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
例如:
1 2 3 4 5 class A {}class B { A a; }
B类中有对象A作为成员,A为对象成员
当其他类对象作为本类成员,构造时先构造类对象,再构造自身 构造先构造其他再构造自身,析构与其相反
静态成员:
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分为:
静态成员变量所有对象共享同一份数据 在编译阶段分配内存 类内声明,类外初始化 静态成员函数所有对象共享同一个函数 静态成员函数只能访问静态成员变量 2.4.3 C++对象模型和this指针 类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象上
空对象占用内存空间为1 C++编译器会给每个空对象分配一个字节的空间,是为了区分空对象占内存的位置 每个空对象也应该有一个独一无二的内存空间
this指针: this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态的成员函数内的一种指针 this指针不需要定义,直接使用即可
this指针用途:
当形参和成员变量同名时,可用this指针来区分 在类的非静态成员函数中返回对象本身,可使用return *this
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <iostream> using namespace std;class Person { public : Person (int age) { this ->age = age; } Person& PersonAddAge (Person &p) { this ->age += p.age; return *this ; } int age; }; void test01 () { Person p1 (18 ) ; cout << "p1的年龄为" << p1. age << endl; } void test02 () { Person p1 (10 ) ; Person p2 (10 ) ; p2. PersonAddAge (p1).PersonAddAge (p1).PersonAddAge (p1); cout << "p2的年龄为" << p2. age << endl; } int main () { test02 (); system ("pause" ); return 0 ; }
空指针访问成员函数: C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
1 2 3 4 if (this ==NULL ){ return ; }
const修饰成员函数:
常函数:
成员函数后加const后我们称为这个函数为常函数 常函数内不可以修改成员属性 成员属性声明时加关键字mutable后,在常函数中依然可以修改 常对象:
声明对象前加const称该对象为常对象 常对象只能调用常函数 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> using namespace std;class Person { public : void showPerson () const { this ->m_B = 100 ; } int m_A; mutable int m_B; }; void test01 () { Person p; p.showPerson (); } void test02 () { const Person p; p.m_B = 100 ; } int main () { test01 (); system ("pause" ); return 0 ; }
2.4.4 友元 友元的目的就是让一个函数或者类 访问另一个类中私有成员
关键字:friend
友元三种实现:
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> using namespace std;class Building { friend void goodGay (Building* building) ; public : Building () { m_SittingRoom = "客厅" ; m_BedRoom = "卧室" ; } public : string m_SittingRoom; private : string m_BedRoom; }; void goodGay (Building *building) { cout << "访问" << building->m_SittingRoom << endl; cout << "访问" << building->m_BedRoom << endl; } void test01 () { Building building; goodGay (&building); } int main () { test01 (); system ("pause" ); return 0 ; }
2.4.5 运算符重载 对已有的运算符重新进行定义,赋予其另一种功能,以适应不用的数据类型
加法重载代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> using namespace std;class Person { public : int m_A; int m_B; }; Person operator +(Person& p1,Person &p2) { Person temp; temp.m_A = p1. m_A + p2. m_A; temp.m_B = p1. m_B + p2. m_B; return temp; } void test01 () { Person p1; p1. m_A = 10 ; p1. m_B = 10 ; Person p2; p2. m_A = 10 ; p2. m_B = 10 ; Person p3 = p1 + p2; cout << "p3.m_A=" << p3. m_A << endl; cout << "p3.m_B=" << p3. m_B << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
左移重载: 作用:可以输出自定义数据类型 不会利用成员函数重载<<
运算符,因为无法实现cout在左边 只能利用全局函数重载左移运算符 左移重载代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> using namespace std;class Person { friend ostream& operator <<(ostream& cout, Person& p); public : Person (int a,int b) { m_A = a; m_B = b; } private : int m_A; int m_B; }; ostream& operator <<(ostream &cout,Person &p) { cout << "m_A=" << p.m_A << "m_B=" << p.m_B; return cout; } void test01 () { Person p (10 ,10 ) ; cout << p<<endl; } int main () { test01 (); system ("pause" ); return 0 ; }
递增重载: 作用:通过重载递增运算符,实现自己的整型数据 递增重载代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> using namespace std;class MyInteger { friend ostream& operator <<(ostream& cout, MyInteger myint); public : MyInteger () { m_Num = 0 ; } MyInteger& operator ++() { m_Num++; return *this ; } MyInteger operator ++(int ) { MyInteger temp = *this ; m_Num++; return temp; } private : int m_Num; }; ostream& operator <<(ostream& cout, MyInteger myint) { cout <<myint.m_Num; return cout; } void test01 () { MyInteger myint; cout << ++myint << endl; cout << myint << endl; } void test02 () { MyInteger myint1; cout << myint1++ << endl; cout << myint1 << endl; } int main () { test01 (); test02 (); system ("pause" ); return 0 ; }
赋值重载: C++编译器至少给一个类添加4个函数 1.默认构造函数(无参,函数体为空) 2.默认析构函数(无参,函数体为空) 3.默认拷贝构造函数,对属性进行值拷贝 4.赋值运算符 operator=,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题 赋值重载代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> using namespace std;class Person { public : Person (int age) { m_Age = new int (age); } ~Person () { if (m_Age!= NULL ) { delete m_Age; m_Age = NULL ; } } Person& operator =(Person& p) { if (m_Age != NULL ) { delete m_Age; m_Age = NULL ; } m_Age = new int (*p.m_Age); return *this ; } int *m_Age; }; void test01 () { Person p1 (18 ) ; Person p2 (20 ) ; p2 = p1; cout << "p1的年龄为" << *p1. m_Age << endl; cout << "p2的年龄为" << *p2. m_Age << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
关系运算符重载: 作用:重载关系运算符,可以让俩个自定义类型对象进行对比操作 关系运算符重载代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> using namespace std;class Person { public : Person (string name,int age) { m_Age = age; m_Name = name; } bool operator ==(Person& p) { if (this ->m_Name == p.m_Name && this ->m_Age == p.m_Age) { return true ; } return false ; } bool operator !=(Person& p) { if (this ->m_Name == p.m_Name && this ->m_Age == p.m_Age) { return false ; } return true ; } string m_Name; int m_Age; }; void test01 () { Person p1 ("Tom" , 18 ) ; Person p2 ("Tom" , 18 ) ; if (p1 == p2) { cout << "p1与p2相等" << endl; } else { cout << "p1与p2不相等" << endl; } if (p1 != p2) { cout << "p1与p2不相等" << endl; } else { cout << "p1与p2相等" << endl; } } int main () { test01 (); system ("pause" ); return 0 ; }
函数调用运算符重载:
函数调用运算符()也可以发生重载 由于重载后使用的方式非常像函数的调用,因此称为仿函数 仿函数没有固定写法,非常灵活 小括号重载代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std;#include <string> class MyPrint { public : void operator () (string test) { cout << test << endl; } }; void test01 () { MyPrint myPrint; myPrint ("hello world" ); } int main () { test01 (); system ("pause" ); return 0 ; }
总结: 1.对于内置的数据类型的表达式的运算符是不可能改变的 2.不要滥用运算符重载
2.4.6 继承 继承是面向对象三大特性之一
好处:减少重复代码
基本语法: class 子类 : 继承方式 父类
子类也称为派生类 父类也称为基类
派生类中的成员,包含俩大部分: 一类是从基类继承过来的,一类是自己增加的成员 从基类继承过来的表现其共性,而新增的成员体现了其个性
继承方式一共有三种:
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <iostream> using namespace std;class Base1 { public : int m_A; protected : int m_B; private : int m_C; }; class Son1 :public Base1{ public : void func () { m_A = 10 ; m_B = 10 ; } }; void test01 () { Son1 s1; s1. m_A = 100 ; } class Base2 { public : int m_A; protected : int m_B; private : int m_C; }; class Son2 :protected Base2{ public : void func () { m_A = 10 ; m_B = 10 ; } }; void test02 () { Son2 s2; } class Base3 { public : int m_A; protected : int m_B; private : int m_C; }; class Son3 :private Base3{ public : void func () { m_A = 10 ; m_B = 10 ; } }; void test03 () { Son3 s3; } int main () { system ("pause" ); return 0 ; }
继承中的对象模型: 父类中所有非静态成员属性都会被子类继承下去 父类中的私有成员属性 是被编译器隐藏了 所以访问不到 但是确实被继承下去了
查看对象模型:打开开发人员命令行工具找到对应.cpp所在的目录,输入cl /dl reportSingleClassLayout所属类名 "对应.cpp"
继承中的构造和析构顺序: 先构造父类,再构造子类;析构与构造顺序相反 子类继承父类后,当创建子类对象,也会调用父类的构造函数
继承同名成员(函数)处理方式:
访问子类同名成员(函数) 直接访问即可 访问父类同名成员(函数) 需要加作用域 s.Base::m_A
如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数 如果想访问到父类中隐藏的同名成员函数,需要加作用域
继承同名静态成员处理方式: 静态成员和非静态成员出现同名,处理方式一致
可以通过对象访问,也可以通过类名访问
访问子类同名成员(函数) 直接访问即可 访问父类同名成员(函数) 需要加作用域对象:s.Base::m_A
类名:Son::Base::m_A
如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数 如果想访问到父类中隐藏的同名成员函数,需要加作用域
多继承语法: C++允许一个类继承多个类
语法:class 子类 : 继承方式 父类1, 继承方式 父类2...
多继承可能会引发父类中有同名成员出现,需要加作用域区分
当父类中出现同名函数,需要加作用域区分
C++实际开发中不建议用多继承
菱形继承: 概念:
俩个派生类B、C继承同一个基类A 又有某个类D同时继承俩个派生类 这种继承方式被称为菱形继承,或者钻石继承 问题: 1.某个类D使用数据时,会产生二义性 2.D继承了A的俩份数据,我们其实只需要一份就行 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> using namespace std;class Animal { public : int m_Age; }; class Sheep :virtual public Animal{ }; class Tuo :virtual public Animal{ }; class SheepTuo :public Sheep, public Tuo{ }; void test01 () { SheepTuo st; st.Sheep::m_Age = 18 ; st.Tuo::m_Age = 28 ; cout << "Sheep::m_Age=" << st.Sheep::m_Age << endl; cout << "Tuo::m_Age=" << st.Tuo::m_Age << endl; cout << "st.m_Age=" << st.m_Age << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
2.4.7 多态 多态是C++面向对象三大特性之一
多态分为俩类:
静态多态:函数重载和运算符重载属于静态重载,复用函数名 动态多态:派生类和虚函数实现运行时多态 条件:1.有继承关系 2.子类重写父类的虚函数 重写 函数返回值类型 函数名 参数列表 完全相同 使用: 父类的指针或者引用执行子类对象 静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址 动态多态的函数地址晚绑定 - 运行阶段确定函数地址 案例: 计算器类 案例描述: 分别利用普通写法和多态技术,设计实现俩个操作数进行运算的计算机类
多态的优点:
代码组织结构清晰 可读性强 利于前期和后期的扩展以及维护 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 #include <iostream> using namespace std;#include <string> class Calulator { public : int getResult (string oper) { if (oper == "+" ) { return m_Num1 + m_Num2; } else if (oper=="-" ) { return m_Num1 - m_Num2; } else if (oper=="*" ) { return m_Num1 * m_Num2; } } int m_Num1; int m_Num2; }; void test01 () { Calulator c; c.m_Num1 = 10 ; c.m_Num2 = 10 ; cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult ("+" ) << endl; cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult ("-" ) << endl; cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult ("*" ) << endl; } class AbstractCalculator { public : virtual int getResult () { return 0 ; } int m_Num1; int m_Num2; }; class AddCalculator :public AbstractCalculator{ public : int getResult () { return m_Num1+m_Num2; } }; class SubCalculator :public AbstractCalculator{ public : int getResult () { return m_Num1 - m_Num2; } }; class MulCalculator :public AbstractCalculator{ public : int getResult () { return m_Num1 * m_Num2; } }; void test02 () { AbstractCalculator* abc = new AddCalculator; abc->m_Num1 = 10 ; abc->m_Num2 = 12 ; cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult () << endl; delete abc; abc = new SubCalculator; abc->m_Num1 = 10 ; abc->m_Num2 = 12 ; cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult () << endl; delete abc; abc = new MulCalculator; abc->m_Num1 = 10 ; abc->m_Num2 = 12 ; cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult () << endl; delete abc; } int main () { test01 (); test02 (); system ("pause" ); return 0 ; }
纯虚函数和抽象类:
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数
纯虚函数语法: virtual 返回值类型 函数名 (参数列表) = 0;
当类中有了纯虚函数,这个类就称为抽象类
抽象类特点:
无法实例化对象 子类必须重写抽象类中的纯虚函数,否则也属于抽象类 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> using namespace std;class Base { public : virtual void func () = 0 ; }; class Son :public Base{ public : virtual void func () { cout << "func函数调用" << endl; } }; void test01 () { Base* base = new Son; base->func (); delete base; } int main () { test01 (); system ("pause" ); return 0 ; }
虚析构和纯虚析构:
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构
或者纯虚析构
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象 都需要有具体的函数实现 虚析构和纯虚析构共性:
虚析构语法: virtual ~类名(){}
纯虚析构语法:virtual ~类名() = 0;
类名::~类名(){}
如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
案例: 电路组装 案例描述: 电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储) 将每个零件封装出抽象基类,并且提供不同厂商生产不同的零件,例如Intel厂商和Lenovo厂商 创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口 测试时组装三台不同的电脑进行工作
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 #include <iostream> using namespace std;class CPU { public : virtual void calculator () = 0 ; }; class VideoCard { public : virtual void display () = 0 ; }; class Memory { public : virtual void storage () = 0 ; }; class Computer { public : Computer (CPU* cpu, VideoCard* vc, Memory* mem) { m_cpu = cpu; m_vc = vc; m_mem = mem; } void work () { m_cpu->calculator (); m_vc->display (); m_mem->storage (); } ~Computer () { if (m_cpu != NULL ) { delete m_cpu; m_cpu = NULL ; } if (m_vc != NULL ) { delete m_vc; m_vc = NULL ; } if (m_mem != NULL ) { delete m_mem; m_mem = NULL ; } } private : CPU* m_cpu; VideoCard* m_vc; Memory* m_mem; }; class IntelCPU :public CPU{ virtual void calculator () { cout << "Intel Cpu开始计算" << endl; } }; class IntelVideoCard :public VideoCard{ virtual void display () { cout << "Intel 显卡开始显示" << endl; } }; class IntelMemory :public Memory{ virtual void storage () { cout << "Intel 内存条开始存储" << endl; } }; class LenovoCPU :public CPU{ virtual void calculator () { cout << "Lenovo CPU开始计算" << endl; } }; class LenovoVideoCard :public VideoCard{ virtual void display () { cout << "Lenovo 显卡开始显示" << endl; } }; class LenovoMemory :public Memory{ virtual void storage () { cout << "Lenovo 内存条开始存储" << endl; } }; void test01 () { CPU* intelCpu = new IntelCPU; VideoCard* intelCard = new IntelVideoCard; Memory* intelMem = new IntelMemory; Computer* computer1 = new Computer (intelCpu, intelCard, intelMem); computer1->work (); delete computer1; Computer* computer2 = new Computer (new LenovoCPU, new LenovoVideoCard, new LenovoMemory); computer2->work (); delete computer2; } int main () { test01 (); system ("pause" ); return 0 ; }
2.5 文件操作 程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放 通过文件可以将数据持久化
C++中对文件操作需要包含头文件<fstream>
文件类型分为俩种: 1.文本文件
- 文件以文本ASCII码
形式存储在计算机中 2.二进制文件
- 文件以文本的二级制
形式存储在计算机中,用户一般不能直接读懂他们
操作文件的三大类: 1.ofstream: 写操作 2.ifstream: 读操作 3.fstream : 读写操作
2.5.1 文本文件 一.写文件 步骤: 1.包含头文件#include<fstream>
2.创建流对象ofstream ofs;
3.打开文件ofs.open("文件路径",打开方式);
4.写数据ofs<<"写入的数据"
5.关闭文件ofs.close();
文件打开方式:
打开方式 解释 ios::in 为读文件而打开文件 ios::out 为写文件而打开文件 ios::ate 初始位置:文件尾 ios::app 追加方式写文件 ios::trunc 如果文件存在先删除,再创建 ios::binary 二进制方式
注意:文件打开方式可以配合使用,利用|
操作符 例如:用二进制方式写文件 ios::binary | ios::out
二.读文件
步骤:
1.包含头文件 #include<fstream>
2.创建流对象 ifstream ifs;
3.打开文件并判断文件是否打开成功 ifs.open("文件路径",打开方式);"
4.读数据 四种方式读取
5.关闭文件 ifs.close();
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> using namespace std;#include <fstream> #include <string> void test01 () { ofstream ofs; ofs.open ("test.txt" ,ios::out); ofs << "姓名:张三" << endl; ofs << "性别:男" << endl; ofs << "年龄:18" << endl; ofs.close (); } void test02 () { ifstream ifs; ifs.open ("test.txt" , ios::in); if (!ifs.is_open ()) { cout << "文件打开失败" << endl; return ; } char c; while ((c=ifs.get ())!=EOF) { cout << c << endl; } ifs.close (); } int main () { test02 (); system ("pause" ); return 0 ; }
2.5.2 二进制文件 打开方式:ios::binary
1.写文件主要利用流对象调用成员函数write 函数原型: ostream& write(const char * buffer,int len);
参数:字符指针buffer指向内存中一段空间,len是读写的字节数
2.读文件主要利用流对象调用成员函数read 函数原型: istream& read(char * buffer,int len);
参数:字符指针buffer指向内存中一段空间,len是读写的字节数
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> using namespace std;#include <fstream> #include <string> class Person { public : char m_Name[64 ]; int m_Age; }; void test03 () { ofstream ofs; ofs.open ("person.txt" , ios::out | ios::binary); Person p = { "张三" ,18 }; ofs.write ((const char *)&p, sizeof (Person)); ofs.close (); } void test04 () { ifstream ifs; ifs.open ("person.txt" , ios::in | ios::binary); if (!ifs.is_open ()) { cout << "文件打开失败" << endl; return ; } Person p; ifs.read ((char *)&p, sizeof (Person)); cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl; ifs.close (); } int main () { test04 (); system ("pause" ); return 0 ; }
案例: 职工管理系统 案例描述:利用C++来实现一个基于多态的职工管理系统
公司中职工分为三种:普通职工、经理、老板,显示信息时,需要显示职工编号、职工姓名、职工岗位、以及职责
普通职工职责:完成经理交给的任务 经理职责:完成老板交给的任务,并下发任务给员工 老板职责:管理公司所有事物
管理系统需要实现的功能如下:
退出管理系统:退出当前管理系统 void ExitSystem();
增加职工信息:实现批量添加职工功能,将信息录入到文件中,职工信息为:职工编号、姓名、部门编号 显示职工信息:显示公司内部所有职工信息 删除离职职工:按照编号删除指定的职工 修改职工信息:按照编号修改职工个人信息 查找职工信息:按照职工的编号或者职工的姓名进行查找相关的人员信息 按照编号排序:按照职工编号进行排序,排序规则由用户指定 清空所有文档:清空文件中记录的所有职工信息(清空前需要再次确定,防止误删) 代码:
创建管理类workerManager:
与用户沟通的菜单界面 void Show_Menu();
对职工增删改查的操作 与文件的读写交互 workerManager.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #pragma once #include <iostream> using namespace std;#include "worker.h" #include "employee.h" #include "manager.h" #include "boss.h" #include <fstream> #define FILENAME "empFile.txt" class WorkerManager { public : WorkerManager (); void Show_Menu () ; void ExitSystem () ; int m_EmpNum; Worker** m_EmpArray; void Add_Emp () ; void save () ; bool m_FileIsEmpty; int get_EmpNum () ; void init_Emp () ; void Show_Emp () ; void Del_Emp () ; int IsExist (int id) ; void Mod_Emp () ; void Find_Emp () ; void Sort_Emp () ; void Clean_File () ; ~WorkerManager (); };
workerManager.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 #include "workerManager.h" WorkerManager::WorkerManager () { ifstream ifs; ifs.open (FILENAME, ios::in); if (!ifs.is_open ()) { cout << "文件不存在" << endl; this ->m_EmpNum = 0 ; this ->m_EmpArray = NULL ; this ->m_FileIsEmpty = true ; ifs.close (); return ; } char ch; ifs >> ch; if (ifs.eof ()) { cout << "文件为空" << endl; this ->m_EmpNum = 0 ; this ->m_EmpArray = NULL ; this ->m_FileIsEmpty = true ; ifs.close (); return ; } int num = this ->get_EmpNum (); cout << "职工人数为:" << num << endl; this ->m_EmpNum = num; this ->m_EmpArray = new Worker * [this ->m_EmpNum]; this ->init_Emp (); } void WorkerManager::Show_Menu () { cout << "******************************" << endl; cout << "**** 欢迎使用职工管理系统 ****" << endl; cout << "******* 0.退出管理系统 *******" << endl; cout << "******* 1.添加职工信息 *******" << endl; cout << "******* 2.显示职工信息 *******" << endl; cout << "******* 3.删除离职职工 *******" << endl; cout << "******* 4.修改职工信息 *******" << endl; cout << "******* 5.查找职工信息 *******" << endl; cout << "******* 6.按照编号排序 *******" << endl; cout << "******* 7.清空所用文档 *******" << endl; cout << "******************************" << endl; cout << endl; } void WorkerManager::ExitSystem () { cout << "欢迎下次使用" << endl; system ("pause" ); exit (0 ); } void WorkerManager::Add_Emp () { cout << "请输入添加职工数量:" << endl; int addNum = 0 ; cin >> addNum; if (addNum>0 ) { int newSize = this ->m_EmpNum + addNum; Worker** newSpace = new Worker * [newSize]; if (this ->m_EmpArray!=NULL ) { for (int i = 0 ; i < this ->m_EmpNum; i++) { newSpace[i] = this ->m_EmpArray[i]; } } for (int i = 0 ; i < addNum; i++) { int id; string name; int dSelect; cout << "请输入第" << i + 1 << "个新职工编号:" << endl; cin >> id; cout << "请输入第" << i + 1 << "个新职工姓名:" << endl; cin >> name; cout << "请选择该职工岗位:" << endl; cout << "1.普通员工" << endl; cout << "2.经理" << endl; cout << "3.老板" << endl; cin >> dSelect; Worker* worker = NULL ; switch (dSelect) { case 1 : worker = new Employee (id, name, 1 ); break ; case 2 : worker = new Manager (id, name, 2 ); break ; case 3 : worker = new Boss (id, name, 3 ); break ; default : break ; } newSpace[this ->m_EmpNum + i] = worker; } delete [] this ->m_EmpArray; this ->m_EmpArray = newSpace; this ->m_EmpNum = newSize; this ->m_FileIsEmpty = false ; cout << "成功添加" << addNum << "名新职工" << endl; this ->save (); } else { cout << "输入有误" << endl; } system ("pause" ); system ("cls" ); } int WorkerManager::get_EmpNum () { ifstream ifs; ifs.open (FILENAME, ios::in); int id; string name; int dId; int num = 0 ; while (ifs>>id&&ifs>>name&&ifs>>dId) { num++; } return num; } void WorkerManager::save () { ofstream ofs; ofs.open (FILENAME, ios::out); for (int i = 0 ; i < this ->m_EmpNum; i++) { ofs << this ->m_EmpArray[i]->m_Id << " " << this ->m_EmpArray[i]->m_Name << " " << this ->m_EmpArray[i]->m_DeptId << endl; } ofs.close (); } void WorkerManager::init_Emp () { ifstream ifs; ifs.open (FILENAME, ios::in); int id; string name; int dId; int index = 0 ; while (ifs >> id && ifs >> name && ifs >> dId) { Worker* worker = NULL ; if (dId==1 ) { worker = new Employee (id, name, dId); }else if (dId == 2 ) { worker = new Manager (id, name, dId); } else { worker = new Boss (id, name, dId); } this ->m_EmpArray[index] = worker; index++; } ifs.close (); } void WorkerManager::Show_Emp () { if (this ->m_FileIsEmpty) { cout << "文件不存在或者记录为空" << endl; } else { for (int i = 0 ; i < m_EmpNum; i++) { this ->m_EmpArray[i]->showInfo (); } } system ("pause" ); system ("cls" ); } void WorkerManager::Del_Emp () { if (this ->m_FileIsEmpty) { cout << "文件不存在或记录为空" << endl; } else { cout << "请输入想要删除职工的编号:" << endl; int id = 0 ; cin >> id; int index = this ->IsExist (id); if (index!=-1 ) { for (int i = index; i < this ->m_EmpNum-1 ; i++) { this ->m_EmpArray[i] = this ->m_EmpArray[i + 1 ]; } this ->m_EmpNum--; this ->save (); cout << "删除成功" << endl; } else { cout << "未找到职工,删除失败" << endl; } } system ("pause" ); system ("cls" ); } int WorkerManager::IsExist (int id) { int index = -1 ; for (int i = 0 ; i < this ->m_EmpNum; i++) { if (this ->m_EmpArray[i]->m_Id == id) { index = i; break ; } } return index; } void WorkerManager::Mod_Emp () { if (this ->m_FileIsEmpty) { cout << "文件不存在或记录为空" << endl; } else { cout << "请输入要修改职工的编号:" << endl; int id; cin >> id; int ret = this ->IsExist (id); if (ret!=-1 ) { delete this ->m_EmpArray[ret]; int newId = 0 ; string newName = "" ; int dSelect = 0 ; cout << "查找到" << id << "号职工,请输入新的职工号:" << endl; cin >> newId; cout << "请输入新的姓名:" << endl; cin >> newName; cout << "请输入新的岗位:" << endl; cout << "1.普通员工" << endl; cout << "2.经理" << endl; cout << "3.老板" << endl; cin >> dSelect; Worker* worker = NULL ; switch (dSelect) { case 1 : worker = new Employee (newId, newName, dSelect); break ; case 2 : worker = new Manager (newId, newName, dSelect); break ; case 3 : worker = new Boss (newId, newName, dSelect); break ; default : break ; } this ->m_EmpArray[ret] = worker; cout << "修改成功" << endl; this ->save (); } else { cout << "查无此人,修改失败" << endl; } } system ("pause" ); system ("cls" ); } void WorkerManager::Find_Emp () { if (this ->m_FileIsEmpty) { cout << "文件不存在或记录为空" << endl; } else { cout << "请输入查找的方式:" << endl; cout << "1.按职工编号查找" << endl; cout << "2.按职工姓名查找" << endl; int select = 0 ; cin >> select; if (select == 1 ) { int id; cout << "请输入查找的职工编号:" << endl; cin >> id; int ret = IsExist (id); if (ret != -1 ) { cout << "查找成功!该职工信息如下:" << endl; this ->m_EmpArray[ret]->showInfo (); } else { cout << "查无此人" << endl; } } else if (select == 2 ) { string name; cout << "请输入查找的职工姓名:" << endl; cin >> name; bool flag = false ; for (int i = 0 ; i < m_EmpNum; i++) { if (this ->m_EmpArray[i]->m_Name==name) { cout << "查找成功!职工编号为:" << this ->m_EmpArray[i]->m_Id << "号职工信息如下" << endl; flag = true ; this ->m_EmpArray[i]->showInfo (); } } if (flag == false ) { cout << "查找失败,查无此人" << endl; } } else { cout << "输入有误" << endl; } } system ("pause" ); system ("cls" ); } void WorkerManager::Sort_Emp () { if (this ->m_FileIsEmpty) { cout << "文件不存在或记录为空" << endl; system ("pause" ); system ("cls" ); } else { cout << "请选择排序方式" << endl; cout << "1.按照职工号升序" << endl; cout << "2.按照职工号降序" << endl; int select = 0 ; cin >> select; for (int i = 0 ; i < m_EmpNum; i++) { int minOrMax = i; for (int j = i+1 ; j < this ->m_EmpNum; j++) { if (select == 1 ) { if (this ->m_EmpArray[minOrMax]->m_Id > this ->m_EmpArray[j]->m_Id) { minOrMax = j; } } else if (select == 2 ) { if (this ->m_EmpArray[minOrMax]->m_Id < this ->m_EmpArray[j]->m_Id) { minOrMax = j; } } else { cout << "输入有误" << endl; } } if (i!=minOrMax) { Worker* temp = this ->m_EmpArray[i]; this ->m_EmpArray[i] = this ->m_EmpArray[minOrMax]; this ->m_EmpArray[minOrMax] = temp; } } cout << "排序成功,排序后的结果为:" << endl; this ->save (); this ->Show_Emp (); } } void WorkerManager::Clean_File () { cout << "确认清空?" << endl; cout << "1.确定" << endl; cout << "2.返回" << endl; int select = 0 ; cin >> select; if (select == 1 ) { ofstream ofs (FILENAME,ios::trunc) ; ofs.close (); if (this ->m_EmpArray != NULL ) { for (int i = 0 ; i < this ->m_EmpNum; i++) { delete this ->m_EmpArray[i]; this ->m_EmpArray[i] = NULL ; } delete [] this ->m_EmpArray; this ->m_EmpArray = NULL ; this ->m_EmpNum = 0 ; this ->m_FileIsEmpty = true ; } cout << "清空成功" << endl; } system ("pause" ); system ("cls" ); } WorkerManager::~WorkerManager () { if (this ->m_EmpArray!=NULL ) { for (int i = 0 ; i < this ->m_EmpNum; i++) { if (this ->m_EmpArray != NULL ) { delete this ->m_EmpArray[i]; } } delete [] this ->m_EmpArray; this ->m_EmpArray = NULL ; } }
创建抽象职工类:
职工的分类:普通职工、经理、老板 将三种职工抽象到一个类(worker)中,利用多态管理不同职工种类 职工的属性:职工编号、职工姓名、职工所在部门编号 职工的行为:岗位职责描述,获取岗位名称
worker.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #pragma once #include <iostream> using namespace std;#include <string> class Worker { public : virtual void showInfo () = 0 ; virtual string getDeptName () = 0 ; int m_Id; string m_Name; int m_DeptId; };
老板类:
boss.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #pragma once #include <iostream> using namespace std;#include "worker.h" class Boss :public Worker{ public : Boss (int id, string name, int dId); virtual void showInfo () ; virtual string getDeptName () ; };
boss.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "boss.h" Boss::Boss (int id, string name, int dId) { this ->m_Id = id; this ->m_Name = name; this ->m_DeptId = dId; } void Boss::showInfo () { cout << "职工编号:" << this ->m_Id << "\t\t职工姓名:" << this ->m_Name << "\t\t岗位:" << this ->getDeptName () << "\t\t岗位职责: 管理公司所有事务" << endl; } string Boss::getDeptName () { return string ("老板" ); }
普通员工类:
employee.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #pragma once #include <iostream> using namespace std;#include "worker.h" class Employee :public Worker{ public : Employee (int id,string name,int dId); virtual void showInfo () ; virtual string getDeptName () ; };
employee.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "employee.h" Employee::Employee (int id, string name, int dId) { this ->m_Id = id; this ->m_Name = name; this ->m_DeptId = dId; } void Employee::showInfo () { cout << "职工编号:" << this ->m_Id << "\t\t职工姓名:" << this ->m_Name << "\t\t岗位:" << this ->getDeptName () << "\t\t岗位职责: 完成经理交给的任务" << endl; } string Employee::getDeptName () { return string ("员工" ); }
经理类:
manager.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #pragma once #include <iostream> using namespace std;#include "worker.h" class Manager :public Worker{ public : Manager (int id, string name, int dId); virtual void showInfo () ; virtual string getDeptName () ; };
manager.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "manager.h" Manager::Manager (int id, string name, int dId) { this ->m_Id = id; this ->m_Name = name; this ->m_DeptId = dId; } void Manager::showInfo () { cout << "职工编号:" << this ->m_Id << "\t\t职工姓名:" << this ->m_Name << "\t\t岗位:" << this ->getDeptName () << "\t\t岗位职责: 完成老板交给的任务,并下发任务给员工" << endl; } string Manager::getDeptName () { return string ("经理" ); }
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> using namespace std;#include "workerManager.h" #include "worker.h" ; #include "employee.h" #include "manager.h" #include "boss.h" int main () { WorkerManager wm; int choice = 0 ; while (true ) { wm.Show_Menu (); cout << "请输入你的选择:" << endl; cin >> choice; switch (choice) { case 0 : wm.ExitSystem (); break ; case 1 : wm.Add_Emp (); break ; case 2 : wm.Show_Emp (); break ; case 3 : wm.Del_Emp (); break ; case 4 : wm.Mod_Emp (); break ; case 5 : wm.Find_Emp (); break ; case 6 : wm.Sort_Emp (); break ; case 7 : wm.Clean_File (); break ; default : system ("cls" ); break ; } } system ("pause" ); return 0 ; }
3 C++提高编程 泛型编程
和STL
3.1 模板 模板就是建立通用的模具,大大提高复用性
特点:
模板不可以直接使用,它只是一个框架 模板的通用并不是万能的 3.1.1 函数模板 C++另一种编程思想称为泛型编程
,主要利用的技术就是模板 C++提供俩种模板机制:函数模板
和类模板
函数模板作用: 建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型
来代表
语法:template<typename T> 函数声明或定义
解释: template --声明创建模板 typename --表面其后面的符号是一种数据类型,可以用class代替 T --通用的数据类型,名称可以替换,通常为大写字母
注意事项:
自动类型推导,必须推导出一致的数据类型T才可以使用 模板必须要确定出T的数据类型才可以使用 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <iostream> using namespace std;template <typename T>void mySwap (T& a, T& b) { T temp = a; a = b; b = temp; } template <class T>void func () { cout << "func调用" << endl; } void swapInt (int & a, int & b) { int temp = a; a = b; b = temp; } void swapDouble (double & a, double & b) { double temp = a; a = b; b = temp; } void test01 () { int a = 10 ; int b = 20 ; mySwap <int >(a, b); cout << "a=" << a << endl; cout << "b=" << b << endl; double c = 1.1 ; double d = 2.2 ; swapDouble (c, d); cout << "c=" << c << endl; cout << "d=" << d << endl; func <int >(); } int main () { test01 (); system ("pause" ); return 0 ; }
普通函数与函数模板的区别:
普通函数调用时可以发生自动类型转换(隐式类型转换) 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换 如果利用显式指定类型的方式,可以发生隐式类型转换 建议使用显式指定类型
普通函数与函数模板调用规则: 1.如果函数模板和普通函数都可以实现,优先调用普通函数 2.可以通过空模板参数列表来强制调用函数模板 func<>();
3.函数模板也可以发生重载 4.如果函数模板可以产生更好的匹配,优先调用函数模板
模板局限性: 传入参数为数组或者自定义数据类型,就无法正常使用 解决方法:用特定数据类型提供具体化操作 重载 template<> bool myCompare(Person &p1,Person &p2){函数实现}
3.1.2 类模板 作用:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型
来表示。
语法:template<typename T> 类
解释: template --声明创建模板 typename --表面其后面的符号是一种数据类型,可以用class代替 T --通用的数据类型,名称可以替换,通常为大写字母
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> using namespace std;#include <string> template <class NameType ,class AgeType >class Person { public : Person (NameType name,AgeType age) { this ->m_Name = name; this ->m_Age = age; } void showPerson () { cout << "name:" << this ->m_Name << "age:" << this ->m_Age << endl; } NameType m_Name; AgeType m_Age; }; void test01 () { Person<string, int >p1 ("张三" , 78 ); p1. showPerson (); } int main () { test01 (); system ("pause" ); return 0 ; }
类模板和函数模板区别: 1.类模板没有自动类型推导的使用方式,只能用显式指定类型 2.类模板在模板参数列表中可以有默认参数
类模板中成员函数创建时机:
普通类中成员函数一开始就创建 类模板中成员函数在调用时才创建 类模板对象做函数参数 三种传入方式: 1.指定传入的类型 --直接显示对象的数据类型 2.参数模板化 --将对象中的参数变为模板进行传递 3.整个类模板化 --将这个对象类型模板化进行传递
代码:
分文件编写: 问题: 类模板中成员函数创建时期是在调用阶段,导致文件编写时调用不到 解决方法: 1.直接包含.cpp源文件
person.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 #include "person.h" template <class T1 , class T2 >Person<T1, T2>::Person (T1 name, T2 age) { this ->m_Name = name; this ->m_Age = age; } template <class T1 , class T2 >void Person<T1, T2>::showPerson (){ cout << "姓名:" << this ->m_Name << " 年龄:" << this ->m_Age << endl; }
person.h
1 2 3 4 5 6 7 8 9 10 11 12 #pragma once #include <iostream> using namespace std;template <class T1 , class T2 >class Person { public : Person (T1 name, T2 age); void showPerson () ; T1 m_Name; T2 m_Age; };
2.将声明和实现写在同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
person.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #pragma once #include <iostream> using namespace std;#include <string> template <class T1 , class T2 >class Person { public : Person (T1 name, T2 age); void showPerson () ; T1 m_Name; T2 m_Age; }; template <class T1 , class T2 >Person<T1, T2>::Person (T1 name, T2 age) { this ->m_Name = name; this ->m_Age = age; } template <class T1 , class T2 >void Person<T1, T2>::showPerson (){ cout << "姓名:" << this ->m_Name << " 年龄:" << this ->m_Age << endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <iostream> using namespace std;#include "person.hpp" void printPerson1 (Person<string ,int >&p) { p.showPerson (); } void test01 () { Person<string, int >p ("张三" , 78 ); printPerson1 (p); } template <class T1,class T2>void printPerson2 (Person<T1, T2>& p) { p.showPerson (); cout << "T1的类型为:" << typeid (T1).name () << endl; cout << "T2的类型为:" << typeid (T2).name () << endl; } void test02 () { Person<string, int >p ("李四" , 98 ); printPerson2 (p); } template <class T>void printPerson3 (T& p) { p.showPerson (); cout << "T的类型为:" << typeid (T).name () << endl; } void test03 () { Person<string, int >p ("王五" , 8 ); printPerson3 (p); } int main () { test03 (); system ("pause" ); return 0 ; }
查看数据类型:typeid(T).name()
类模板与继承:
当子类继承的父类是一个类模板时,子类在声明的时候要指定出父类中T的类型 如果不指定,编译器无法给子类分配内存 如果想灵活指定出父类中T的类型,子类也需变为类模板 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> using namespace std;template <class T >class Base { T m; }; class Son :public Base<int >{ }; void test01 () { Son s1; } template <class T1 ,class T2 >class Son2 :public Base<T2>{ public : Son2 () { cout << "T1的类型为" << typeid (T1).name () << endl; cout << "T2的类型为" << typeid (T2).name () << endl; } T1 obj; }; void test01 () { Son2<int , char >s2; } int main () { test01 (); system ("pause" ); return 0 ; }
类模板成员函数类外实现
类模板和友元:
全局函数类内实现 --直接在类内声明友元就行了 全局函数类外实现 --需要提前让编译器知道全局函数的存在 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> using namespace std;#include <string> template <class T1 , class T2 >class Person ;template <class T1, class T2>void print2 (Person<T1, T2> p) { cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl; } template <class T1 ,class T2 >class Person { friend void print1 (Person<T1,T2> p) { cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl; } friend void print2<>(Person<T1, T2> p); public : Person (T1 name, T2 age); { this ->m_Name = name; this ->m_Age = age; } private : T1 m_Name; T2 m_Age; }; void test01 () { Person<string, int >p ("Tom" , 20 ); print2 (p); } int main () { test01 (); system ("pause" ); return 0 ; }
案例: 类模板案例 案例描述:
可以对内置函数类型以及自定义数据类型进行存储 将数组中的数据存储到堆区 构造函数中可以传入数组的容量 提供对应的拷贝构造函数以及operator=防止浅拷贝问题 提供尾插法和尾删法对数组中的数据进行增加和删除 可以通过下标的方式访问数组中的元素 可以获取数组中当前元素个数和数组的容量 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 #pragma once #include <iostream> using namespace std;template <class T >class MyArray { public : MyArray (int capacity) { this ->m_Capacity = capacity; this ->m_Size = 0 ; this ->pAddress = new T[this ->m_Capacity]; } MyArray (const MyArray& arr) { this ->m_Capacity = arr.m_Capacity; this ->m_Size = arr.m_Size; this ->pAddress = new T[arr.m_Capacity]; for (int i = 0 ; i < this ->m_Size; i++) { this ->pAddress[i] = arr.pAddress[i]; } } MyArray& operator =(const MyArray& arr) { if (this ->pAddress != NULL ) { delete [] this ->pAddress; this ->pAddress = NULL ; this ->m_Capacity = 0 ; this ->m_Size = 0 ; } this ->m_Capacity = arr.m_Capacity; this ->m_Size = arr.m_Size; this ->pAddress = new T[arr.m_Capacity]; for (int i = 0 ; i < this ->m_Size; i++) { this ->pAddress[i] = arr.pAddress[i]; } return *this ; } void Push_Back (const T& val) { if (this ->m_Capacity==this ->m_Size) { return ; } this ->pAddress[this ->m_Size] = val; this ->m_Size++; } void Pop_Back () { if (this ->m_Size == 0 ) { return ; } this ->m_Size--; } T& operator [](int index) { return this ->pAddress[index]; } int getCapacity () { return this ->m_Capacity; } int getSize () { return this ->m_Size; } ~MyArray () { if (this ->pAddress!=NULL ) { delete [] this ->pAddress; this ->pAddress = NULL ; } } private : T* pAddress; int m_Capacity; int m_Size; };
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <iostream> using namespace std;#include "MyArray.hpp" void printArray (MyArray <int >& arr) { for (int i = 0 ;i < arr.getSize ();i++) { cout << arr[i] << endl; } } void Test01 () { MyArray <int >arr1 (5 ); for (int i = 0 ;i < 5 ;i++) { arr1. Push_Back (i); } cout << "arr1:" << endl; printArray (arr1); cout << "arr1容量:" << arr1. getCapacity () << " 大小:" << arr1. getSize () << endl; MyArray <int >arr2 (arr1); arr2. Pop_Back (); cout << "arr2:" << endl; printArray (arr2); cout << "arr2容量:" << arr2. getCapacity () << " 大小:" << arr2. getSize () << endl; } class Person { public : Person () {}; Person (string name, int age) { this ->m_Name = name; this ->m_Age = age; } string m_Name; int m_Age; }; void printPersonArray (MyArray<Person>& arr) { for (int i = 0 ;i < arr.getSize ();i++) { cout << "姓名:" << arr[i].m_Name << " 年龄:" << arr[i].m_Age << endl; } } void Test02 () { MyArray<Person> arr (10 ) ; Person p1 ("孙悟空" , 999 ) ; Person p2 ("韩信" , 20 ) ; Person p3 ("李白" , 25 ) ; Person p4 ("安其拉" , 18 ) ; Person p5 ("蔡文姬" , 12 ) ; arr.Push_Back (p1); arr.Push_Back (p2); arr.Push_Back (p3); arr.Push_Back (p4); arr.Push_Back (p5); printPersonArray (arr); cout << "arr容量:" << arr.getCapacity () << " 大小:" << arr.getSize () << endl; } int main () { Test02 (); system ("pause" ); return 0 ; }
3.2 STL STL(标准模块库) 从广义上分为:容器
、算法
、迭代器
容器和算法间通过迭代器进行无缝连接
STL六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器 1.容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。 2.算法:各种常用的算法,如sort、find、copy、for_each等。 3.迭代器:扮演了容器与算法之间的胶合剂。 4.仿函数:行为类似函数,可作为算法的某种策略。 5.适配器(配接器):一种用来修饰容器或者仿函数或迭代器接口的东西。 6.空间配置器:负责空间的配置与管理。
3.2.1 容器算法迭代器 容器:置物之所也 STL容器就是运用最广泛的一些数据结构实现出来 常见的数据结构:数组、链表、树、栈、队列、集合、映射表 等 分类:
序列式容器:强调值的排序,序列式容器中的每个元素有固定的位置。 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。 算法:问题之解法也 有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法 分类:
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等 非质变算法:是指运算过程中不会更改区间内的元素的内容。例如查找,计数,遍历,寻求极值等等 迭代器:容器和算法之间粘合剂 提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。 每个容器都有自己专属的迭代器 迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
迭代器种类:
种类 功能 支持运算 输入迭代器 对数据的只读访问 只读,支持++、==、!=
输出迭代器 对数据的只写访问 只写,支持++
前向迭代器 读写操作,并能向前推进迭代器 读写,支持++、==、!=
双向迭代器 读写操作,并能向前和向后操作 读写,支持++、--
随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 读写,支持++、--、[n]、-n、<、<=、>、>=
常用的容器中迭代器种类为双向迭代器和随机访问迭代器
vector存放内置数据类型 容器: vector
算法: for_each
迭代器: vector<int>::iterator
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <iostream> #include <vector> #include <algorithm> using namespace std;void MyPrint (int val) { cout << val << endl; } void test01 () { vector<int > v; v.push_back (10 ); v.push_back (20 ); v.push_back (30 ); v.push_back (40 ); vector<int >::iterator itBegin = v.begin (); vector<int >::iterator itEnd = v.end (); while (itBegin!=itEnd) { cout << *itBegin << endl; itBegin++; } for (vector<int >::iterator it = v.begin ();it != v.end ();it++) { cout << *it << endl; } for_each(v.begin (), v.end (), MyPrint); } void test02 () { vector<vector<int >>v; vector<int >v1; vector<int >v2; vector<int >v3; vector<int >v4; for (int i = 0 ;i < 4 ;i++) { v1. push_back (i + 1 ); v2. push_back (i + 2 ); v3. push_back (i + 3 ); v4. push_back (i + 4 ); } v.push_back (v1); v.push_back (v2); v.push_back (v3); v.push_back (v4); for (vector<vector<int >>::iterator it= v.begin (); it!=v.end (); it++) { for (vector<int >::iterator vit = (*it).begin ();vit != (*it).end (); vit++) { cout << *vit << " " ; } cout << endl; } } int main () { test02 (); system ("pause" ); return 0 ; }
vector也可以存放自定义数据类型
3.2.2 string容器 本质: string 是C++风格的字符串,而string本质上是一个类
string 和 char* 区别:
char* 是一个指针 string 是一个类,类内部封装了char* ,管理这个字符串,是一个 char* 型的容器。 特点: string类内部封装了很多成员方法 例如:查找find,拷贝copy,删除delete,替换replace,插入insert string 管理char* 所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
string构造函数 构造函数原型:
string();
//创建一个空的字符串 例如:string str;string(const char* s);
//使用字符串s初始化string(const string& str);
//使用一个string对象初始化另一个string对象string(int n,char c);
//使用n个字符c初始化string赋值操作 功能:给string字符串进行赋值
赋值的函数原型:
string& operator=(const char* s);
//给char* 类型字符串 赋值给当前字符串string& operator=(const string &s);
//把字符串s赋给当前的字符串string& operator=(char c);
//字符赋值给当前的字符串string& assign(const char *s);
//把字符串s赋给当前的字符串string& assign(const char *s,int n);
//把字符串s的前n个字符赋值给当前的字符串string& assign(const string &s);
//把字符串s赋给当前字符串string& assign(int n,char c);
//把n个字符c赋给当前字符串operator=方式比较实用
string字符串拼接 功能:实现在字符串末尾拼接字符串
函数原型:
string& operator+=(const char* str);
//重载+=操作符string& operator+=(const char c);
//重载+=操作符string& operator+=(const string& str);
//重载+=操作符string& append(const char *s);
//把字符串s拼接到当前字符串结尾string& append(const char *s,int n);
//把字符串s的前n个字符连接到当前的字符串结尾string& append(const string &s);
//同operator+=(const string& str)string& append(const string &s,int pos,int n);
//字符串s中从pos开始的n个字符连接到字符串结尾string查找和替换 功能:
查找:查找指定字符串是否存在 替换:在指定的位置替换字符串 函数原型:
int find(const string& str,int pos = 0) const;
//查找str第一次出现位置,从pos开始查找int find(const char* s,int pos = 0) const;
//查找s第一次出现位置,从pos开始查找int find(const char* s,int pos,int n) const;
//从pos位置查找s的前n个字符第一次位置int find(const char c,int pos = 0) const;
//查找字符c第一次出现位置int rfind(const string& str,int pos = npos) const;
//查找str最后一次位置,从pos开始查找int rfind(const char* s,int pos = npos) const;
//查找s最后一次出现位置,从pos开始查找int rfind(const char* s,int pos,int n) const;
//从pos位置查找s的前n个字符最后一次位置int rfind(const char c,int pos = 0) const;
//查找字符c最后一次出现位置string& replace(int pos,int n,const string& str);
//替换从pos开始n个字符为字符串strstring& replace(int pos,int n,const char* s);
//替换从pos开始的n个字符为字符串sstring字符串比较 功能:字符串之间的比较
比较方式: 字符串比较是按字符的ASCII码进行对比
函数原型:
int compare(const string &s) const;
//与字符串s比较int compare(const char *s) const;
//与字符串s比较主要用于比较俩个字符串是否相等
string字符存取 存取方式:
char& operator[](int n);
//通过[]
方式取字符char& at(int n);
//通过at方法获取字符string插入和删除 功能:对string字符串进行插入和删除字符操作
函数原型:
string& insert(int pos,const char* s);
//插入字符串string& insert(int pos,const string& str);
//插入字符串string& insert(int pos,int n,char c);
//在指定位置插入n个字符cstring& erase(int pos,int n = npos);
//删除从pos开始的n个字符插入和删除下标从0开始
string子串 功能:从字符串中获取想要的子串
函数原型:
string substr(int pos = 0,int n = npos) const;
//返回由pos开始的n个字符组成的字符串代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 #include <iostream> #include <string> using namespace std;void test01 () { string s1; const char * str = "hello world" ; string s2 (str) ; cout << "s2=" << s2 << endl; string s3 (s2) ; cout << "s3=" << s3 << endl; string s4 (10 , 'a' ) ; cout << "s4=" << s4 << endl; } void test02 () { string str1; str1 = "hello world" ; cout << "str1=" << str1 << endl; string str2; str2 = str1; cout << "str2=" << str2 << endl; string str3; str3 = 's' ; cout << "str3=" << str3 << endl; string str4; str4. assign ("hello c++" ); cout << "str4=" << str4 << endl; string str5; str5. assign ("hello c++" ,5 ); cout << "str5=" << str5 << endl; string str6; str6. assign (str5); cout << "str6=" << str6 << endl; string str7; str7. assign (10 , 'w' ); cout << "str7=" << str7 << endl; } void test03 () { string str1 = "我" ; str1 += "爱学习" ; cout << "str1=" << str1 << endl; str1 += ':' ; cout << "str1=" << str1 << endl; string str2=" java,python" ; str1 += str2; cout << "str1=" << str1 << endl; string str3 = "I" ; str3. append (" Love" ); cout << "str3=" << str3 << endl; str3. append (" You," ,4 ); cout << "str3=" << str3 << endl; str3. append (str2, 0 , 5 ); cout << "str3=" << str3 << endl; } void test04 () { string str1 = "abcdefgde" ; int pos=str1.f ind("de" ); cout << "find:pos=" << pos << endl; pos = str1. rfind ("de" ); cout << "rfind:pos=" << pos << endl; } void test05 () { string str1 = "abcdefg" ; str1. replace (1 , 3 , "1111" ); cout << "str1=" << str1 << endl; } void test06 () { string str1 = "xbcd" ; string str2 = "abcd" ; if (str1. compare (str2) == 0 ) { cout << "str1=str2" << endl; } else if (str1. compare (str2) > 0 ) { cout << "str1>str2" << endl; } else { cout << "str1<str2" << endl; } } void test07 () { string str1 = "hello" ; str1[0 ] = 'x' ; str1. at (1 ) = 'x' ; for (int i = 0 ;i < str1. size ();i++) { cout << str1[i] << " " ; } cout << endl; for (int i = 0 ;i < str1. size ();i++) { cout << str1. at (i) << " " ; } cout << endl; } void test08 () { string str1 = "hello" ; str1. insert (1 , "111" ); cout << "str1=" << str1 << endl; str1. erase (1 , 3 ); cout << "str1=" << str1 << endl; } void test09 () { string str = "abcdefg" ; string subStr = str.substr (1 , 3 ); cout << "subStr=" << subStr << endl; } int main () { test09 (); system ("pause" ); }
3.2.3 vector容器 功能: vector数据结构与数组非常相似,也称为单端数组
vector与普通数组区别: 不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展:
并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间 vector容器的迭代器是支持随机访问的迭代器 vector构造函数 功能:创建vector容器
函数原型:
vector<T> v;
//采用模板实现类实现,默认构造函数vector(v.begin(),v.end());
//将v[begin(),end())区间中的元素拷贝给本身。vector(n,elem);
//构造函数将n个elem拷贝给本身。vector(const vector &vec);
//拷贝构造函数vector赋值操作 功能:给vector容器进行赋值
函数原型:
vector& operator=(const vector &vec);
//重载等号操作符assign(begin,end);
//将[begin,end)区间中的数据拷贝赋值给本身assign(n,elem);
//将n个elem拷贝赋值给本身vector容量和大小 功能:对vector容器的容量和大小操作
函数原型:
empty();
//判断容器是否为空capacity();
//容器的容量size();
//返回容器中元素的个数resize(int num);
//重新指定容器的长度为num。若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除resize(int num,elem);
//重新指定容器的长度为num。若容器变长,则以elem填充新位置;如果容器变短,则末尾超出容器长度的元素被删除vector插入和删除 功能:对vector容器进行插入、删除操作
函数原型:
push_back(ele);
//尾部插入元素elepop_back();
//删除最后一个元素insert(const_iterator pos,ele);
//迭代器指向位置pos插入元素eleinsert(const_iterator pos,int count,ele);
//迭代器指向位置pos插入count个元素eleerase(const_iterator pos);
//删除迭代器指定的元素erase(const_iterator start,const_iterator end);
//删除迭代器从start到end之间的元素clear();
//删除容器中所有元素vector数据读取 功能:对vector中的数据的存取操作
函数原型:
at(int idx);
//返回索引idx所指的数据operator[];
//返回索引idx所指的数据front();
//返回容器中第一个元素back();
//返回容器中最后一个元素vector互换容器 功能:实现俩个容器中元素进行互换
函数原型:
vector预留空间 功能:减少vector在动态扩展容量时的扩展次数
函数原型:
reserve(int len);
//容器预留len个元素长度,预留位置不初始化,元素不可访问代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 #include <iostream> using namespace std;#include <vector> void printVector (vector<int >&v) { for (vector<int >::iterator it = v.begin ();it != v.end ();it++) { cout << *it << " " ; } cout << endl; } void test01 () { vector<int >v1; for (int i = 0 ; i < 10 ; i++) { v1. push_back (i); } printVector (v1); vector<int >v2 (v1. begin (), v1. end ()); printVector (v2); vector<int >v3 (10 , 100 ); printVector (v3); vector<int >v4 (v3); printVector (v4); } void test02 () { vector<int >v1; for (int i = 0 ; i < 10 ; i++) { v1. push_back (i); } printVector (v1); vector<int >v2; v2 = v1; printVector (v2); vector<int >v3; v3. assign (v1. begin (), v1. end ()); printVector (v3); vector<int >v4; v4. assign (10 , 100 ); printVector (v4); } void test03 () { vector<int >v1; for (int i = 0 ; i < 10 ; i++) { v1. push_back (i); } printVector (v1); if (v1. empty ()) { cout << "v1为空" << endl; } else { cout << "v1不为空" << " 容量为 " << v1. capacity () << " 大小 " << v1. size () << endl; } v1. resize (15 ,100 ); printVector (v1); v1. resize (5 ); printVector (v1); } void test04 () { vector<int >v1; v1. push_back (10 ); v1. push_back (20 ); v1. push_back (30 ); v1. push_back (40 ); v1. push_back (50 ); printVector (v1); v1. pop_back (); printVector (v1); v1. insert (v1. begin (), 100 ); printVector (v1); v1. insert (v1. begin (), 2 , 1000 ); printVector (v1); v1. erase (v1. begin ()); printVector (v1); v1. clear (); printVector (v1); } void test05 () { vector<int >v1; for (int i = 0 ; i < 10 ; i++) { v1. push_back (i); } for (int i = 0 ;i < v1. size ();i++) { cout << v1[i] << " " ; } cout << endl; for (int i = 0 ;i < v1. size ();i++) { cout << v1. at (i)<< " " ; } cout << endl; cout << "第一个元素:" << v1.f ront() << endl; cout << "最后一个元素:" << v1. back () << endl; } void test06 () { vector<int >v1; for (int i = 0 ; i < 10 ; i++) { v1. push_back (i); } cout << "交换前:" << endl; printVector (v1); vector<int >v2; for (int i = 10 ; i >0 ; i--) { v2. push_back (i); } printVector (v2); cout << "交换后:" << endl; v1. swap (v2); printVector (v1); printVector (v2); } void test07 () { vector<int >v; for (int i = 0 ; i < 100000 ; i++) { v.push_back (i); } cout << "v 的容量:" << v.capacity () << endl; cout << "v 的大小:" << v.size () << endl; v.resize (3 ); cout << "v 的容量:" << v.capacity () << endl; cout << "v 的大小:" << v.size () << endl; vector <int >(v).swap (v); cout << "v 的容量:" << v.capacity () << endl; cout << "v 的大小:" << v.size () << endl; } void test08 () { vector<int >v; v.reserve (100000 ); int num = 0 ; int * p = NULL ; for (int i = 0 ; i < 100000 ; i++) { v.push_back (i); if (p != &v[0 ]) { p = &v[0 ]; num++; } } cout << num << endl; } int main () { test08 (); system ("pause" ); return 0 ; }
3.2.4 deque容器 功能:双端数组,可以对头端进行插入和删除操作
deque与vector区别:
vector对于头部的插入删除效率低,数据量越大,效率越低 deque相对而言,对头部的插入删除速度比vector快 vector访问元素时的速度比deque快,这和俩者内部实现有关 deque内部工作原理: deque内部有个中控器
,维护每段缓冲区中的内容,缓冲区中存放真实数据 中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
deque构造函数 功能:deque容器构造
函数原型:
deque<T>deqT;
//默认构造形式deque(beg,end);
//构造函数将[beg,end)区间中的元素拷贝给本身deque(n,elem);
//构造函数将n个elem拷贝给本身deque(const deque &deq);
//拷贝构造函数deque赋值操作 功能:给deque容器赋值
函数原型:
deque& operator=(const deque &deq);
//重载等号操作符assign(beg,end);
//将[beg,end)区间中的数据拷贝赋值给本身assign(n,elem);
//将n个elem拷贝赋值给本身deque大小操作 功能:对deque容器的大小进行操作
函数原型:
deque.empty();
//判断容器是否为空deque.size();
//返回容器中元素的个数deque.resize(num);
//重新指定容器的长度为num。若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除deque.resize(num,elem);
//重新指定容器的长度为num。若容器变长,则以elem填充新位置;如果容器变短,则末尾超出容器长度的元素被删除deque插入和删除 功能:向deque容器中插入和删除数据
函数原型:
俩端插入操作:
push_back(elem);
//在容器尾部插入一个数据push_front(elem);
//在容器头部插入一个数据pop_back();
//删除容器最后一个数据pop_front();
//删除容器第一个数据指定位置操作:
insert(pos,elem);
//在pos位置插入一个elem元素的拷贝,返回新数据的位置insert(pos,n,elem);
//在pos位置插入n个elem数据,无返回值insert(pos,beg,end);
//在pos位置插入[beg,end)区间的数据,无返回值clear();
//删除容器中所有数据erase(beg,end);
//删除[beg,end)区间的数据,返回下一个数据的位置erase(pos);
//删除pos位置的数据,返回下一个数据的位置deque数据存取 功能:对deque中的数据的存取操作
函数原型:
at(int idx);
//返回索引idx所指的数据operator[];
//返回索引idx所指的数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素deque排序 功能:利用算法实现对deque容器进行排序
算法:
sort(iterator beg,iterator end)
//对beg和end区间内元素进行排序对于支持随机访问的迭代器的容器,都可以利用sort算法直接对其进行排序
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 #include <iostream> using namespace std;#include <deque> #include <algorithm> void printDeque (const deque<int >& d) { for (deque<int >::const_iterator it = d.begin ();it != d.end ();it++) { cout << *it << " " ; } cout << endl; } void test01 () { deque<int >d1; for (int i = 0 ;i < 10 ;i++) { d1. push_back (i); } printDeque (d1); deque<int >d2 (d1. begin (),d1. end ()); printDeque (d2); deque<int >d3 (10 ,100 ); printDeque (d3); deque<int >d4 (d3); printDeque (d4); } void test02 () { deque<int >d1; for (int i = 0 ;i < 10 ;i++) { d1. push_back (i); } printDeque (d1); deque<int >d2; d2 = d1; printDeque (d2); deque<int >d3; d3. assign (d1. begin (), d1. end ()); printDeque (d3); deque<int >d4; d4. assign (10 , 100 ); printDeque (d4); } void test03 () { deque<int >d1; for (int i = 0 ;i < 10 ;i++) { d1. push_back (i); } printDeque (d1); if (d1. empty ()) { cout << "d1为空" << endl; } else { cout << "d1不为空" << endl; cout << "d1大小为:" << d1. size () << endl; } d1. resize (15 ,1 ); printDeque (d1); d1. resize (5 ); printDeque (d1); } void test04 () { deque<int >d1; d1. push_back (10 ); d1. push_back (20 ); d1. push_front (100 ); d1. push_front (200 ); printDeque (d1); d1. pop_back (); printDeque (d1); d1. pop_front (); printDeque (d1); } void test05 () { deque<int >d1; d1. push_back (10 ); d1. push_back (20 ); d1. push_front (100 ); d1. push_front (200 ); printDeque (d1); d1. insert (d1. begin (), 1000 ); printDeque (d1); d1. insert (d1. begin (), 2 , 10000 ); printDeque (d1); deque<int >d2; d2. push_back (1 ); d2. push_back (2 ); d2. push_back (3 ); d1. insert (d1. begin (), d2. begin (), d2. end ()); printDeque (d1); deque<int >::iterator it = d1. begin (); it++; d1. erase (it); printDeque (d1); d1. clear (); printDeque (d1); } void test06 () { deque<int >d1; d1. push_back (10 ); d1. push_back (20 ); d1. push_back (30 ); d1. push_front (100 ); d1. push_front (200 ); d1. push_front (300 ); for (int i = 0 ;i < d1. size ();i++) { cout << d1[i] << " " ; } cout << endl; for (int i = 0 ;i < d1. size ();i++) { cout << d1. at (i) << " " ; } cout << endl; cout << "第一个元素:" << d1.f ront() << endl; cout << "最后一个元素:" << d1. back () << endl; } void test07 () { deque<int >d; d.push_back (10 ); d.push_back (20 ); d.push_back (30 ); d.push_front (100 ); d.push_front (200 ); d.push_front (300 ); printDeque (d); sort (d.begin (), d.end ()); cout << "排序后:" << endl; printDeque (d); } int main () { test07 (); system ("pause" ); return 0 ; }
案例: 评委打分 案例描述: 有5名选手:选手ABCDE,10个评委分别对每一个选手打分,去除最高分,去除评委最低分,求平均分
实现步骤: 1.创建五名选手,放到vector中 2.遍历vector容器,取出每一位选手,执行for循环,可以把10个评分分存到deque容器中 3.sort算法对deque容器中的分数进行排序,取出最高和最低分 4.deque容器遍历一遍,累加总分 5.获取平均分
代码
3.2.5 stack容器 stack是一种先进后出的数据结构,它只有一个出口
栈中只有顶端元素才可以被访问,因此不允许有遍历的行为
常用接口 构造函数:
stack<T> stk;
//stack采用模板类实现,stack对象的默认构造形式stack(const stack &stk);
//拷贝构造函数赋值操作:
stack& operator=(const stack &stk);
//重载等号操作符数据存取:
push(elem);
//向栈顶添加元素pop();
//从栈顶移除第一个元素top();
//返回栈顶元素大小操作:
empty();
//判断栈是否为空size();
//返回栈的大小代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std;#include <stack> void test01 () { stack<int >s; s.push (10 ); s.push (20 ); s.push (30 ); s.push (40 ); if (!s.empty ()) { cout << "栈顶元素: " << s.top () << endl; s.pop (); } cout << "栈的大小: " << s.size () << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
3.2.6 queue容器 queue是一种先进先出的数据结构,他有2个出口
队列容器只允许从一端新增数据,从另一端移除数据
队列中只有队头元素和队尾元才可以被访问,因此不允许有遍历的行为
常用接口 构造函数:
queue<T> que;
//queue采用模板类实现,queue对象的默认构造形式queue(const queue &que);
//拷贝构造函数赋值操作:
queue& operator=(const queue &que);
//重载等号操作符数据存取:
push(elem);
//向队尾添加元素pop();
//从队头移除第一个元素back();
//返回最后一个元素front();
//返回第一个元素大小操作:
empty();
//判断队列是否为空size();
//返回队列的大小代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> using namespace std;#include <queue> void test01 () { queue<int >q; q.push (10 ); q.push (20 ); q.push (30 ); q.push (40 ); if (!q.empty ()) { cout << "第一个元素: " << q.front () << endl; cout << "最后一个元素: " << q.back () << endl; q.pop (); cout << "第一个元素: " << q.front () << endl; cout << "最后一个元素: " << q.back () << endl; } cout << "队列的大小: " << q.size () << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
3.2.7 list容器 功能:将数据进行链式存储
链表
是一种物理存储单元上非连续的存储结构,数据结构的逻辑顺序是通过链表中的指针链接实现的
优点:可以对任意位置快速插入和删除 缺点:遍历速度没有数组快,占用空间比数组大 链表组成:链表由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是一个双向循环链表
list中的迭代器只支持前移和后移,属于双向迭代器
list优点:
采用动态存储分配,不会造成内存浪费和溢出 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素 list缺点:
插入和删除操作都不会造成原有list迭代器的失效,这在vector中是不成立的
STL中List和vector是最常使用的容器
list构造函数 功能:创建list容器
函数原型:
list<T> lst;
//list采用模板类实现,对象的默认构造形式list(beg,end);
//构造函数将[beg,end)区间的元素拷贝给本身list(n,elem);
////构造函数将n个elem拷贝给本身list(const list &lst);
//拷贝构造函数list赋值和交换 功能:给list容器进行赋值,以及交换list容器
函数原型:
assign(beg,end);
//将[beg,end)区间中的数据拷贝赋值给本身assign(n,elem);
//将n个elem拷贝赋值给本身list& operator=(const list &lst);
//重载等号操作符swap(lst);
//将lst与本身元素互换list大小操作 功能:对list容器的大小进行操作
函数原型:
size();
//返回容器中元素的个数empty();
//判断容器是否为空resize(num);
//重新指定容器的长度为num。若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除resize(num,elem);
//重新指定容器的长度为num。若容器变长,则以elem填充新位置;如果容器变短,则末尾超出容器长度的元素被删除list插入和删除 功能:对list容器数据进行插入、删除操作
函数原型:
push_back(elem);
//在容器尾部加入一个元素pop_back();
//删除容器中最后一个元素push_front(elem);
//在容器开头插入一个元素pop_front();
//从容器开头移除第一个元素insert(pos,elem);
//在pos位置插入elem元素的拷贝,返回新数据的位置insert(pos,n,ele);
//在pos位置插入n个elem数据,无返回值clear();
//删除容器中所有数据erase(beg,end);
//删除[beg,end)区间的数据,返回下一个数据的位置erase(pos);
//删除pos位置的数据,返回下一个数据的位置remove(elem);
//删除容器中所有与elem值匹配的元素list数据存取 功能:对list中的数据进行存取
函数原型:
front();
//返回第一个元素back();
//返回最后一个元素list反转和排序 功能:将容器中的元素反转,以及将容器中的数据进行排序
函数原型:
reverse();
//反转链表sort();
//链表排序代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 #include <iostream> using namespace std;#include <list> void printList (const list<int >& l) { for (list<int >::const_iterator it = l.begin ();it != l.end ();it++) { cout << *it << " " ; } cout << endl; } void test01 () { list<int >l1; l1. push_back (10 ); l1. push_back (20 ); l1. push_back (30 ); l1. push_back (40 ); printList (l1); list<int >l2 (l1. begin (), l1. end ()); printList (l2); list<int >l3 (l2); printList (l3); list<int >l4 (10 , 1000 ); printList (l4); } void test02 () { list<int >l1; l1. push_back (10 ); l1. push_back (20 ); l1. push_back (30 ); l1. push_back (40 ); printList (l1); list<int >l2; l2 = l1; printList (l2); list<int >l3; l3. assign (l2. begin (), l2. end ()); printList (l3); list<int >l4; l4. assign (10 , 1000 ); printList (l4); } void test03 () { list<int >l1; l1. push_back (10 ); l1. push_back (20 ); l1. push_back (30 ); l1. push_back (40 ); list<int >l2; l2. assign (10 , 1000 ); cout << "交换前:" << endl; printList (l1); printList (l2); cout << endl; l1. swap (l2); cout << "交换后:" << endl; printList (l1); printList (l2); } void test04 () { list<int >l1; l1. push_back (10 ); l1. push_back (20 ); l1. push_back (30 ); l1. push_back (40 ); if (l1. empty ()) { cout << "l1为空" << endl; } else { cout << "l1不为空" << endl; cout << "l1的大小为:" << l1. size () << endl; } l1. resize (10 ); printList (l1); l1. resize (2 ); printList (l1); } void test05 () { list<int >l1; l1. push_back (10 ); l1. push_back (20 ); l1. push_back (30 ); l1. push_front (100 ); l1. push_front (200 ); l1. push_front (300 ); printList (l1); l1. pop_back (); printList (l1); l1. pop_front (); printList (l1); list<int >::iterator it = l1. begin (); l1. insert (++it, 1000 ); printList (l1); it = l1. begin (); l1. erase (++it); printList (l1); l1. push_back (10000 ); l1. push_back (10000 ); l1. push_back (10000 ); printList (l1); l1. remove (10000 ); printList (l1); l1. clear (); printList (l1); } void test06 () { list<int >l1; l1. push_back (10 ); l1. push_back (20 ); l1. push_back (30 ); l1. push_back (40 ); cout << "第一个元素:" << l1.f ront() << endl; cout << "最后一个元素:" << l1. back () << endl; list<int >::iterator it = l1. begin (); } bool myCompare (int val1, int val2) { return val1 > val2; } void test07 () { list<int >l1; l1. push_back (90 ); l1. push_back (30 ); l1. push_back (20 ); l1. push_back (70 ); printList (l1); l1. reverse (); printList (l1); l1. sort (); printList (l1); l1. sort (myCompare); printList (l1); } int main () { test07 (); system ("pause" ); return 0 ; }
案例: list容器排序 案例描述: 将Person自定义数据类型进行排序,Person中属性有姓名、年龄、身高 排序规则: 按照年龄进行升序,如果年龄相同按照身高进行降序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <iostream> using namespace std;#include <list> class Person { public : Person (string name, int age, int height) { this ->m_Name = name; this ->m_Age = age; this ->m_Height = height; } string m_Name; int m_Age; int m_Height; }; bool comparePerson (Person &p1,Person &p2) { if (p1. m_Age == p2. m_Age) { return p1. m_Height > p2. m_Height; } else { return p1. m_Age < p2. m_Age; } } void test01 () { list<Person>L; Person p1 ("刘备" , 35 , 175 ) ; Person p2 ("关羽" , 45 , 180 ) ; Person p3 ("孙权" , 40 , 170 ) ; Person p4 ("赵云" , 25 , 190 ) ; Person p5 ("张飞" , 35 , 160 ) ; Person p6 ("关羽" , 35 , 200 ) ; L.push_back (p1); L.push_back (p2); L.push_back (p3); L.push_back (p4); L.push_back (p5); L.push_back (p6); for (list<Person>::iterator it = L.begin ();it != L.end ();it++) { cout << "姓名: " << (*it).m_Name << " 年龄: " << it->m_Age << " 身高: " << it->m_Height << endl; } cout << "排序后:" << endl; L.sort (comparePerson); for (list<Person>::iterator it = L.begin ();it != L.end ();it++) { cout << "姓名: " << (*it).m_Name << " 年龄: " << it->m_Age << " 身高: " << it->m_Height << endl; } } int main () { test01 (); system ("pause" ); return 0 ; }
3.2.8 set/multiset容器 set: 所有元素都会在插入时被排序
本质:set/multiset属于关联式容器
,底层结构是用二叉树
实现的
set与multiset区别:
set不允许容器中有重复的元素 multiset允许容器中有重复的元素 set构造和赋值 功能:创建set容器以及赋值
构造:
set<T> st;
//默认构造函数set(const set &st);
//拷贝构造函数赋值:
set& operator=(const set &st);
//重载等号操作符set大小和交换 功能:统计set容器大小以及交换set容器
函数原型:
size();
//返回容器中元素的数目empty();
//判断容器是否为空swap(st);
//交换俩个集合容器set插入和删除 功能:set容器进行插入和删除数据
函数原型:
insert(elem);
//在容器中插入元素clear();
//清除所有元素erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器erase(beg,end);
//删除区间[beg,end)的所有元素,返回下一个元素的迭代器erase(elem);
//删除容器中值为elem的元素set查找和统计 功能:对set容器进行查找数据以及统计数据
函数原型:
find(key);
//查找key是否存在,若存在返回该键的元素的迭代器;若不存在返回set.end();count(key);
//统计key的元素个数set与multiset区别 区别:
set不允许插入重复元素,而multiset可以 set插入数据的同时会返回插入结果,表示插入是否成功 multiset不会检测数据,因此可以插入重复数据 pair对组创建 功能:成对出现的数据,利用对组可以返回俩个数据
创建方式:
pair<type,type> p (value1,value2);
pair<type,type> p = make_pair(value1,value2);
set容器排序 1.内置数据类型
2.自定义数据类型
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 #include <iostream> using namespace std;#include <set> #include <string> void printSet (set<int >& s) { for (set<int >::iterator it = s.begin ();it != s.end ();it++) { cout << *it << " " ; } cout << endl; } void test01 () { set<int >s1; s1. insert (10 ); s1. insert (20 ); s1. insert (40 ); s1. insert (30 ); printSet (s1); set<int >s2 (s1); printSet (s2); set<int >s3; s3 = s2; printSet (s3); } void test02 () { set<int >s1; s1. insert (10 ); s1. insert (20 ); s1. insert (40 ); s1. insert (30 ); if (s1. empty ()) { cout << "s1为空" << endl; } else { cout << "s1不为空" << endl; cout << "s1大小为:" << s1. size () << endl; } set<int >s2; s2. insert (1 ); s2. insert (2 ); s2. insert (9 ); s2. insert (3 ); cout << "交换前:" << endl; printSet (s1); printSet (s2); cout << "交换后:" << endl; s1. swap (s2); printSet (s1); printSet (s2); } void test03 () { set<int >s1; s1. insert (10 ); s1. insert (20 ); s1. insert (40 ); s1. insert (30 ); printSet (s1); s1. erase (s1. begin ()); printSet (s1); s1. erase (30 ); printSet (s1); s1. clear (); printSet (s1); } void test04 () { set<int >s1; s1. insert (10 ); s1. insert (20 ); s1. insert (40 ); s1. insert (30 ); s1. insert (30 ); s1. insert (30 ); printSet (s1); set<int >::iterator pos = s1.f ind(30 ); if (pos != s1. end ()) { cout << "找到元素:" << *pos << endl; } else { cout << "没找到" << endl; } int num = s1. count (30 ); cout << num << endl; } void test05 () { set<int >s1; pair<set<int >::iterator,bool > ret = s1. insert (10 ); if (ret.second) { cout << "第一次插入成功" << endl; } else { cout << "第一次插入失败" << endl; } ret = s1. insert (10 ); if (ret.second) { cout << "第二次插入成功" << endl; } else { cout << "第二次插入失败" << endl; } multiset<int >ms; ms.insert (10 ); ms.insert (10 ); for (multiset<int >::iterator it = ms.begin ();it != ms.end ();it++) { cout << *it << " " ; } cout << endl; } void test06 () { pair<string, int >p1 ("Tom" , 20 ); cout << "姓名:" << p1.f irst << "年龄:" << p1. second << endl; pair<string, int >p2 = make_pair ("Jory" , 18 ); cout << "姓名:" << p2.f irst << "年龄:" << p2. second << endl; } class MyCompare { public : bool operator () (int v1,int v2) const { return v1 > v2; } }; void test07 () { set<int >s1; s1. insert (10 ); s1. insert (40 ); s1. insert (20 ); s1. insert (50 ); s1. insert (30 ); printSet (s1); set<int ,MyCompare>s2; s2. insert (10 ); s2. insert (40 ); s2. insert (20 ); s2. insert (50 ); s2. insert (30 ); for (set<int , MyCompare>::iterator it = s2. begin ();it != s2. end ();it++) { cout << *it << " " ; } cout << endl; } class Person { public : Person (string name, int age) { this ->m_Name = name; this ->m_Age = age; } string m_Name; int m_Age; }; class comparePerson { public : bool operator () (const Person& p1, const Person& p2) const { return p1. m_Age > p2. m_Age; } }; void test08 () { set<Person,comparePerson>s; Person p1 ("刘备" , 24 ) ; Person p2 ("关羽" , 28 ) ; Person p3 ("张飞" , 25 ) ; Person p4 ("赵云" , 21 ) ; s.insert (p1); s.insert (p2); s.insert (p3); s.insert (p4); for (set<Person,comparePerson>::iterator it = s.begin ();it != s.end ();it++) { cout << "姓名:" << it->m_Name << " 年龄:" << it->m_Age << endl;; } } int main () { test08 (); system ("pause" ); return 0 ; }
3.2.9 map/multimap容器 map中所有元素都是pair pair中第一个元素位key(键值),起索引作用,第二个元素为value(实值) 所有元素都会根据元素的键值自动排序 本质: map/multimap属于关联式容器
,底层结构是用二叉树实现
优点:
map和multimap区别:
map不允许容器中有重复的key值元素 multimap允许容器中有重复的key值元素 map构造和赋值 功能:对map容器进行构造和赋值操作
构造:
map<T1,T2> mp;
//默认构造函数map(const map &mp);
//拷贝构造函数赋值:
map& operator=(const map &map);
//重载等号操作符map大小和交换 功能:统计map容器大小以及交换map容器
函数原型:
size();
//返回容器中元素的数目empty();
//判断容器是否为空swap(st);
//交换俩个集合容器map插入和删除 功能:map容器进行插入和删除数据
函数原型:
insert(elem);
//在容器中插入元素clear();
//清除所有元素erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器erase(beg,end);
//删除区间[beg,end)的所有元素,返回下一个元素的迭代器erase(key);
//删除容器中值为key的元素map查找和统计 功能:对map容器进行查找数据以及统计数据
函数原型:
find(key);
//查找key是否存在,若存在返回该键的元素的迭代器;若不存在返回map.end();count(key);
//统计key的元素个数map容器排序 默认按照key值进行从小到大排序
利用仿函数
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 #include <iostream> using namespace std;#include <map> #include <string> void printMap (map<int , int >& m) { for (map<int , int >::iterator it = m.begin ();it != m.end ();it++) { cout << "key = " << (*it).first << " value = " << it->second << endl; } cout << endl; } void test01 () { map<int , int >m; m.insert (pair <int , int >(1 , 10 )); m.insert (pair <int , int >(2 , 20 )); m.insert (pair <int , int >(3 , 30 )); m.insert (pair <int , int >(4 , 40 )); printMap (m); map<int , int >m2 (m); printMap (m2); map<int , int >m3; m3 = m2; printMap (m3); } void test02 () { map<int , int >m; m.insert (pair <int , int >(1 , 10 )); m.insert (pair <int , int >(2 , 20 )); m.insert (pair <int , int >(3 , 30 )); m.insert (pair <int , int >(4 , 40 )); if (m.empty ()) { cout << "m为空" << endl; } else { cout << "m不为空" << endl; cout << "m的大小为:" << m.size () << endl; } map<int , int >m2; m2. insert (pair <int , int >(1 , 230 )); m2. insert (pair <int , int >(2 , 130 )); m2. insert (pair <int , int >(3 , 340 )); m2. insert (pair <int , int >(4 , 240 )); cout << "交换前:" << endl; printMap (m); printMap (m2); cout << "交换后:" << endl; m.swap (m2); printMap (m); printMap (m2); } void test03 () { map<int , int >m; m.insert (pair <int , int >(1 , 10 )); m.insert (make_pair (2 , 20 )); m.insert (map<int , int >::value_type (3 , 30 )); m[4 ] = 40 ; printMap (m); m.erase (m.begin ()); printMap (m); m.erase (3 ); printMap (m); m.clear (); printMap (m); } void test04 () { map<int , int >m; m.insert (pair <int , int >(1 , 10 )); m.insert (pair <int , int >(2 , 20 )); m.insert (pair <int , int >(3 , 30 )); map<int , int >::iterator pos = m.find (3 ); if (pos != m.end ()) { cout << "找到元素: key = " << (*pos).first << " value = " << pos->second << endl; } else { cout << "未找到元素" << endl; } int num = m.count (3 ); cout << "num = " << num << endl; } class MyCompare { public : bool operator () (int v1,int v2) const { return v1 > v2; } }; void test05 () { map<int , int ,MyCompare>m; m.insert (pair <int , int >(1 , 10 )); m.insert (pair <int , int >(2 , 20 )); m.insert (pair <int , int >(3 , 30 )); m.insert (pair <int , int >(4 , 40 )); m.insert (pair <int , int >(5 , 50 )); for (map<int , int , MyCompare>::iterator it = m.begin ();it != m.end ();it++) { cout << "key = " << it->first << " value = " << it->second << endl; } cout << endl; } int main () { test05 (); system ("pause" ); return 0 ; }
案例: 员工分组 案例描述:
公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工去哪个部门工作 员工信息有:姓名、工资组成;部门分为:策划、美术、研发 随机给10名员工分配部门和工资 通过multimap进行信息的插入 key(部门编号) value(员工) 分部门显示员工信息 步骤: 1.创建10名员工,放到vector中 2.遍历vector容器,取出每个员工,进行随机分组 3.分组后将员工部门编号作为key,具体员工为value,放入到multimap容器中 4.分部门显示员工信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <iostream> using namespace std;#include <vector> #include <string> #include <map> #include <ctime> #define CEHUA 0 #define MEISHU 1 #define YANFA 2 class Worker { public : string m_Name; int m_Salary; }; void createWorker (vector<Worker>&v) { string nameSeed = "ABCDEFGHIJ" ; for (int i = 0 ;i < 10 ;i++) { Worker worker; worker.m_Name = "员工" ; worker.m_Name += nameSeed[i]; worker.m_Salary = rand () % 10000 + 10000 ; v.push_back (worker); } } void setGroup (vector<Worker>& v, multimap<int , Worker>& m) { for (vector<Worker>::iterator it = v.begin ();it != v.end ();it++) { int deptId = rand () % 3 ; m.insert (make_pair (deptId, *it)); } } void showWorkerByGroup (multimap<int , Worker>& m) { cout << "策划部门:" << endl; multimap<int , Worker>::iterator pos = m.find (CEHUA); int count = m.count (CEHUA); int index = 0 ; for (;pos != m.end () && index < count;pos++, index++) { cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl; } cout << "美术部门:" << endl; pos = m.find (MEISHU); count = m.count (MEISHU); index = 0 ; for (;pos != m.end () && index < count;pos++, index++) { cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl; } cout << "研发部门:" << endl; pos = m.find (YANFA); count = m.count (YANFA); index = 0 ; for (;pos != m.end () && index < count;pos++, index++) { cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl; } } int main () { srand ((unsigned int )time (NULL )); vector<Worker>vWorker; createWorker (vWorker); multimap<int , Worker>mWorker; setGroup (vWorker,mWorker); showWorkerByGroup (mWorker); system ("pause" ); return 0 ; }
3.3 STL函数对象 概念:
重载函数调用操作符的类,其对象常称为函数对象 函数对象使用重载的()
时,行为类似函数调用,也叫仿函数 本质:函数对象是一个类,不是一个函数
使用特点:
函数对象在使用时,可以像普通函数那样调用,可以有参数和返回值 函数对象超出普通函数概念,函数对象可以有自己的状态 函数对象可以作为参数传递 3.3.1 谓词 概念:
返回bool类型
的仿函数称为谓词 如果operator()接收一个参数,那么就叫一元谓词 如果operator()接收二个参数,那么就叫二元谓词 3.3.2 内建函数对象 概念:STL内建了一些函数对象
分类:
用法:
这些仿函数所产生的对象用法和一般函数完全相同 使用内建函数对象,需要引入头文件#include<functional>
算术仿函数 功能描述:
实现四则运算 其中negate是一元运算,其他都是二元运算 仿函数原型:
template<class T> T plus<T>
//加法仿函数template<class T> T minus<T>
//减法仿函数template<class T> T multiplies<T>
//乘法仿函数template<class T> T divides<T>
//除法仿函数template<class T> T modulus<T>
//取模仿函数template<class T> T negate<T>
//取反仿函数1 2 3 4 5 6 negate<int >n; cout << n (50 ) << endl; plus<int >p; cout << p (10 , 20 ) << endl;
关系仿函数 功能描述:
仿函数原型:
template<class T> bool equal_to<T>
//等于template<class T> bool not_equal_to<T>
//不等于template<class T> bool greater<T>
//大于template<class T> bool greater_equal<T>
//大于等于template<class T> bool less<T>
//小于template<class T> bool less_equal<T>
//小于等于逻辑仿函数 功能描述:
仿函数原型:
template<class T> bool logical_and<T>
//逻辑与template<class T> bool logical_or<T>
//逻辑或template<class T> bool logical_not<T>
//逻辑非3.4 STL常用算法 概述:
算法主要是由头文件<algorithm>
<functional>
<numeric>
组成 <algorithm>
是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等<functional>
体积很小,只包括几个在序列上面进行的简单数学运算的模板函数<numeric>
定义了一些模板类,用以声明函数对象3.4.1 常用遍历算法 函数原型:
目标容器需要提前开辟空间
3.4.2 常用查找算法 find
//查找元素
函数原型:
find(iterator beg,iterator end,value);
//按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置 //beg
开始迭代器 //end
结束迭代器 //value
查找的元素find_if
//按条件查找元素
函数原型:
find_if(iterator beg,iterator end,_Pred);
//按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置 //beg
开始迭代器 //end
结束迭代器 //_Pred
函数或者谓词(返回bool类型的仿函数)adjacent_find
//查找相邻重复元素
函数原型:
adjacent_find(iterator beg,iterator end);
//查找相邻重复元素,返回相邻元素的第一个位置的迭代器 //beg
开始迭代器 //end
结束迭代器binary_search
//二分查找法
函数原型:
bool binary_search(iterator beg,iterator end,value);
//查找指定的元素 找到返回true 否则返回false //beg
开始迭代器 //end
结束迭代器 //value
查找的元素count
//统计元素个数
函数原型:
count(iterator beg,iterator end,value);
//统计元素出现次数 //beg
开始迭代器 //end
结束迭代器 //value
统计的元素count_if
//按条件统计元素个数
函数原型:
count_if(iterator beg,iterator end,_Pred);
//按条件统计元素出现次数 //beg
开始迭代器 //end
结束迭代器 //_Pred
谓词3.4.3 常用排序算法 sort
//对容器内元素进行排序
函数原型:
sort(iterator beg,iterator end,_Pred);
//对容器内元素进行排序 //beg
开始迭代器 //end
结束迭代器 //_Pred
谓词random_shuffle
//洗牌 指定范围内的元素随机调整次序
函数原型:
random_shuffle(iterator beg,iterator end);
//指定范围内的元素随机调整次序 //beg
开始迭代器 //end
结束迭代器可以添加随机数种子: srand((unsigned int)time(NULL))
merge
//容器元素合并,并存储到另一个容器中
函数原型:
merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
//注意:俩个容器必须是有序且一致(同升同降)的 //beg1
容器1开始迭代器 //end1
容器1结束迭代器 //beg2
容器2开始迭代器 //end2
容器2结束迭代器 //目标容器需要提前分配空间 //dest
目标容器开始迭代器reverse
//反转指定范围的元素
函数原型:
reverse(iterator beg,iterator end);
//beg
开始迭代器 //end
结束迭代器3.4.4 常用拷贝和构造算法 copy
//容器内指定范围的元素拷贝到另一个容器中
函数原型:
copy(iterator beg,iterator end,iterator dest);
//beg
开始迭代器 //end
结束迭代器 //目标容器需要提前分配空间 //dest
目标容器起始迭代器replace
//将容器内指定范围的旧元素修改为新元素
函数原型:
replace(iterator beg,iterator end,oldvalue,newvalue);
//beg
开始迭代器 //end
结束迭代器 //oldvalue
旧元素 //newvalue
新元素replace_if
//容器内指定范围满足条件的元素替换为新元素
函数原型:
replace_if(iterator beg,iterator end,_pred,newvalue);
//beg
开始迭代器 //end
结束迭代器 //_pred
谓词 //newvalue
新元素swap
//互换俩个容器的元素
函数原型:
swap(container c1,container c2);
//注意c1、c2必须为同一种容器 //c1
容器1 //c2
容器23.4.5 常用算术生成算法 包含头文件#include<numeric>
accumulate
//计算容器元素累计总和
函数原型:
accumulate(iterator beg,iterator end,value);
//beg
开始迭代器 //end
结束迭代器 //value
起始值fill
//向容器中添加元素
函数原型:
fill(iterator beg,iterator end,value);
//beg
开始迭代器 //end
结束迭代器 //value
填充的值3.4.6 常用集合算法 案例: 演讲比赛流程管理系统 比赛规则:
学校举行一场演讲比赛,共有12人
参加。比赛共俩轮
,第一轮为淘汰赛,第二轮为决赛 每名选手都有对应的编号
:如10001 ~ 10012 比赛方式:分组比赛每组6人
每一轮分为俩个小组,整体按照选手编号进行抽签
后顺序演讲 十个评委分别给每个选手打分,去除最高分和最低分,求的平均分为本轮选手的成绩 当小组演讲完后,淘汰组内排名最后的三个选手,前三名晋级
,进入下一轮比赛 第二轮为决赛,前三名胜出
每轮比赛过后需要显示晋级选手的信息
程序功能:
开始演讲比赛:完成整届比赛的流程,每个比赛阶段需要给用户一个提示,用户按任意键后继续下一个阶段 查看往届记录:查看之前比赛前三名结果,每次比赛都会记录到文件中,文件用.csv后缀名保存 清空比赛记录:将文件中数据清空 退出比赛程序:可以退出当前程序 代码
机房预约系统 身份简介
学生代表:申请使用机房 教师:审核学生的预约申请 管理员:给学生老师创建账号 机房简介
1号机房:最大容量20人 2号机房:最大容量50人 3号机房:最大容量100人 申请简介
申请的订单每周由管理员清空 学生可以预约未来一周内的机房使用,预约的日期为周一至周五,预约时需要选择时间段(上午、下午) 教师来审核预约,依据实际情况审核预约通过或者不通过 系统具体要求
首先进入登录界面,可选身份有: 每个身份都需要进型验证后,进入子菜单学生需要输入:学号、姓名、登录密码 老师需要输入:职工号、姓名、登录密码 学生需要输入:管理员姓名、登录密码 学生具体功能申请预约 — 预约机房 查看自身的预约 — 查看自身的预约状态 查看所有预约 — 查看全部预约信息以及预约状态 取消预约 — 取消自身的预约,预约成功或审核中的预约都可以取消 注销登录 — 退出登录 教师具体功能查看所有预约 — 查看全部预约信息以及预约状态 审核预约 — 对学生的预约进行审核 注销登录 — 退出登录 管理员具体功能添加账号 — 添加学生或教师账号,需要检测学生编号和教师职工编号是否重复 查看账号 — 可以选择查看学生或老师的全部信息 查看机房 — 查看所有机房的消息 清空预约 — 清空所有预约记录 注销登录 — 退出登录 代码