写在前面:此博文仅作为个人c++学习笔记,或有错误不建议参考!!!

视频链接:黑马程序员c++教程从0到1

目录

一、第一部分c++基础

1.1 c++初识

1.1.1 hello world

1.1.2 常量和变量

1.1.3 数据类型

1.1.4 运算符

1.2 程序结构

1.2.1 选择结构

1.2.2 循环结构

1.3 数组

1.4 函数

1.5 指针变量

1.6 结构体

二、第二部分c++核心

2.1 内存分区

2.1.1 内存分区模型

2.1.2 程序运行前分区

2.1.3 程序运行后分区

2.2 引用

2.2.1 引用做函数参数

2.2.2 引用做函数返回值

2.2.3 引用本质

2.2.4 常量引用

2.3 函数提高

2.3.1 函数默认参数

2.3.2 函数占位参数

2.3.3 函数重载

2.4 类和对象

2.4.1 封装

2.4.2 对象的初始化和清理

2.4.3 初始化列表

2.4.4 静态成员

2.4.5 c++对象模型和this指针

2.4.6 const修饰成员函数

2.4.7 友元

2.4.8 运算符重载

2.5 继承

2.6 多态

2.7 文件操作

三、第三部分c++提高

3.1 模板

3.1.1 函数模板

3.1.2 类模板

3.2 STL标准模板库

3.2.1 STL 基本概念

3.2.2 容器算法迭代器

3.3 STL常用容器

3.3.1 string容器

3.3.2 vector容器

3.3.3 deque容器

3.3.4 stack堆栈

3.3.5 queue队列

3.3.6 list链表

3.3.7 set/ multiset容器

3.3.8 map/multimap

3.4 STL函数对象

3.4.1 函数对象使用

3.4.2 谓词

3.4.3 内建函数对象

3.5 STL常用算法

3.5.1 常用遍历算法

3.5.2 常用查找算法

3.5.3 常用排序算法

3.5.4 常用拷贝和替换算法

3.5.5 常用算术生成算法

3.5.6 常用集合算法


一、第一部分c++基础

1.1 c++初识

1.1.1 hello world

#include<iostream>
using namespace std;
​
int main() {
​cout << "Hello world" << endl;
​system("pause");
​return 0;
}//单行注释用两个斜杠
//多行注释:鼠标选中内容,ctrl+k+c完成注释,ctrl+k+u放开注释/*多行注释用斜杠星号开始斜杠星号结束*/// using namespace std;是标准命名空间,作用如
// std::cout<< "Hello world" <<std::endl; 变 cout<< "Hello world" <<endl;//system("pause");的作用是请按任意键继续

1.1.2 常量和变量

常量有两种:1、宏常量;2、const修饰的变量;

#define day 7                //宏常量      语法 #define 常量名 常量值
​
int main() {
​cout << "一周里总共有 " << day << " 天" << endl;//day = 8; 报错,宏常量不可以修改
​const int month = 12;   //const修饰变量作为常量cout << "一年里总共有 " << month << " 个月份" << endl;//month = 24; 报错,常量是不可以修改的system("pause");​return 0;
}

变量

int a = 10;             //语法:数据类型  变量名 = 初始值//c++变量必须赋初始值

关键字

变量和常量名可以包含字母、数字、下划线区分大小写,不可以数字作为开头,同时避开关键字

1.1.3 数据类型

c++在创建一个变量或者常量时,必须要指定相应的数据类型,否则无法给变量分配内存

sizeof关键字可以用来查看类型所占内存大小

//sizeof语法              sizeof( 数据类型 / 变量)
cout << "int 类型所占内存空间为: " << sizeof(int) << endl;
cout << "变量a所占内存空间为:    " << sizeof(a) << endl;

转义字符

//c++中一些特殊字符不能直接输出出来,需要用转义字符替代
//转义字符一般都是反斜杠加一个字母组成
cout <<“hello world”<< endl;
cout <<“hello world\n” ;             // \n换行,与endl作用一样cout <<"\\"<< endl;                  // \\输出一个反斜杠\cout <<“aaa\thello world”<< endl;    //  \t水平制表aaa     hello world// aaa占三位\t自动补5个空格凑够八位

字符和字符串

#include<string>                    //使用字符串需要包含字符串的头文件,字符不用
int main() {char ch='a';                    //字符占一个字节string str = "hello world";     //字符用单引号,字符串用双引号cout << str << endl;cout << ch << endl;system("pause"); return 0;
}

字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元

 cout << (int)ch << endl;  //强制转换int数据类型,查看字符a对应的ASCII码ch = 97;                  //可以直接用ASCII给字符型变量赋值,97对应acout << ch << endl;

bool类型

int main() {bool flag = true;cout << flag << endl; // 1flag = false;cout << flag << endl; // 0cout << "size of bool = " << sizeof(bool) << endl; //bool类型占1字节system("pause");return 0;
}

输入cin和输出cout

int a = 0;cout << "请输入整型变量:" << endl;cin >> a;cout << a << endl;

1.1.4 运算符

只有整形才能取模,除法分母不能为零

//加减乘除
int main() {int a1 = 10;int b1 = 3;cout << a1 + b1 << endl;cout << a1 - b1 << endl;cout << a1 * b1 << endl;cout << a1 / b1 << endl;  //两个整数相除结果依然是整数,3double d1 = 0.5;double d2 = 0.25;int d3=0;cout << d1 / d2 << endl;  //两个小数可以相除//cout << d1 / d3 << endl;   报错,除数不可以为0cout << 10 % 3 << endl;   //取模,结果为1double d4 = 3.14;double d5 = 1.1;//cout << d4 % d5 << endl;//小数不可以取模system("pause");return 0;
}

赋值运算符

比较运算符

int main() {int a = 10;int b = 20;cout << (a == b) << endl; // 0 cout << (a != b) << endl; // 1cout << (a > b) << endl; // 0cout << (a <= b) << endl; // 1system("pause");return 0;
}

逻辑运算符

int main() {int a = 10;int b = 10;cout << !a << endl; // 0            逻辑非 真变假,假变真cout << !!a << endl; // 1int c = 10;int d = 0;cout << (c && d) << endl;// 0       逻辑与 同真为真,其它为假cout << (c || d) << endl;// 1       逻辑或 有一个真,就为真system("pause");return 0;
}

1.2 程序结构

  • 顺序结构:程序按顺序执行

  • 选择结构:依据条件是否满足,有选择的执行代码

  • 循环结构:依据条件是否满足,循环多次执行某段代码

1.2.1 选择结构

1、if语句;2、三目运算符;3、switch语句;

单行if

int main() {int score = 0;cout << "请输入一个分数:" << endl;cin >> score;cout << "您输入的分数为: " << score << endl;if (score > 600)            //if判断语句后面不要加分号{cout << "我考上了一本大学!!!" << endl;}system("pause");return 0;
}

多行if,不满足if就执行else

int main() {int score = 0;cout << "请输入考试分数:" << endl;cin >> score;if (score > 600){cout << "我考上了一本大学" << endl;}else{cout << "我未考上一本大学" << endl;}system("pause");return 0;
}

多条件if

 int main() {int score = 0;cout << "请输入考试分数:" << endl;cin >> score;if (score > 600){cout << "我考上了一本大学" << endl;}else if (score > 500){cout << "我考上了二本大学" << endl;}else if (score > 400){cout << "我考上了三本大学" << endl;}else{cout << "我未考上本科" << endl;}system("pause");return 0;
}

嵌套if

int main() {int score = 0;cout << "请输入考试分数:" << endl;cin >> score;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;
}

三目运算符

int main() {int a = 10;int b = 20;int c = 0;c = a > b ? a : b;                   //?为真执行a否则执行bcout << "c = " << c << endl;         //20(a > b ? a : b) = 100;               //C++中三目运算符返回的是变量,可以继续赋值cout << "b = " << b << endl;         //b=100system("pause");return 0;
}

switch语句,if判断条件可以是一个区间例如a>600的情况,switch判断条件是一个值例如a=600时

int main() { //请给电影评分 //10 ~ 9   经典   // 8 ~ 7   非常好// 6 ~ 5   一般// 5分以下 烂片int score = 0;cout << "请给电影打分" << endl;cin >> score;switch (score){case 10:                     //用的冒号不是分号case 9:cout << "经典" << endl;break;case 8:cout << "非常好" << endl;break;case 7:case 6:cout << "一般" << endl;break;default:                     //以上条件都不满足时default退出cout << "烂片" << endl;break;}system("pause");return 0;
}

1.2.2 循环结构

1、while循环;2、do-while循环;3、for循环

while循环

int main() { int num = 0;while (num < 10)            //满足条件就一直执行,直到num >= 10{cout << "num = " << num << endl;  //0 1 2 3 4 5 6 7 8 9 num++;}system("pause");return 0;
}

do-while

int main() {int num = 0;do                           //do while 先执行后判断{cout << num << endl;     //0 1 2 3 4 5 6 7 8 9num++;} while (num < 10);system("pause");return 0;
}

for循环

int main() {for (int i = 0; i < 10; i++){cout << i << endl;       // 0 1 2 3 4 5 6 7 8 9}system("pause");return 0;
}

嵌套for循环

int main() {for (int i = 0; i < 10; i++){for (int j = 0; j < 10; j++){cout << "*" << " ";}cout << endl;}system("pause");return 0;
}

break跳转

int main() {for (int i = 0; i < 10; i++){for (int j = 0; j < 10; j++){if (j == 5){break;           //在嵌套循环语句中使用break,退出内层循环,继续外循环}cout << "*" << " ";}cout << endl;}system("pause");return 0;
}

continue跳转

int main() {for (int i = 0; i < 100; i++){if (i % 2 == 0){continue;        //跳过本次循环中当前步,继续执行循环}cout << i << endl;   // 1 3 5 7 9 11...}system("pause");return 0;
}

go to跳转

int main() {cout << "1" << endl;goto FLAG;               //只输出1 5cout << "2" << endl;cout << "3" << endl;cout << "4" << endl;FLAG:cout << "5" << endl;system("pause");return 0;
}

1.3 数组

所谓数组,就是一个集合,里面存放了相同类型的数据元素,数组由连续的内存位置组成

//一维数组的3中定义方式
int main() {int score[10];   //方式1   数据类型 数组名[元素个数]score[0] = 100;  //方式1   利用下标赋值score[1] = 99;score[2] = 85;cout << score[0] << endl;  //方式1  利用下标输出,注意数组下标从0开始的cout << score[1] << endl;cout << score[2] << endl;//第二种定义方式//数据类型 数组名[元素个数] =  {值1,值2 ,值3 ...};//如果{}内不足10个数据,剩余数据用0补全int score2[10] = { 100, 90,80,70,60,50,40,30,20,10 };//逐个输出//cout << score2[0] << endl;//cout << score2[1] << endl;//可以利用循环进行输出for (int i = 0; i < 10; i++){cout << score2[i] << endl;}//定义方式3//数据类型 数组名[] =  {值1,值2 ,值3 ...};int score3[] = { 100,90,80,70,60,50,40,30,20,10 };for (int i = 0; i < 10; i++){cout << score3[i] << endl;}system("pause");return 0;
}

数组名用途

int main() {//数组名用途//1、可以获取整个数组占用内存空间大小int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };cout << "整个数组所占内存空间为: " << sizeof(arr) << endl;cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl;cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl;//2、可以通过数组名获取到数组首地址cout << "数组首地址为: " << (int)arr << endl;cout << "数组中第一个元素地址为: " << (int)&arr[0] << endl;cout << "数组中第二个元素地址为: " << (int)&arr[1] << endl;//arr = 100; 错误,数组名是常量,因此不可以赋值system("pause");return 0;
}

二维数组

int main() {//方式1  //数组类型 数组名 [行数][列数]int arr[2][3];arr[0][0] = 1;arr[0][1] = 2;arr[0][2] = 3;arr[1][0] = 4;arr[1][1] = 5;arr[1][2] = 6;for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){cout << arr[i][j] << " ";}cout << endl;}//方式2 //数据类型 数组名[行数][列数] = { {数据1,数据2 } ,{数据3,数据4 } };int arr2[2][3] ={{1,2,3},                //用逗号不是分号{4,5,6}};//方式3//数据类型 数组名[行数][列数] = { 数据1,数据2 ,数据3,数据4  };int arr3[2][3] = { 1,2,3,4,5,6 }; //方式4 //数据类型 数组名[][列数] = { 数据1,数据2 ,数据3,数据4  };int arr4[][3] = { 1,2,3,4,5,6 };system("pause");return 0;
}

二维数组名用途

int main() {int arr[2][3] ={{1,2,3},{4,5,6}};cout << "二维数组大小: " << sizeof(arr) << endl;cout << "二维数组一行大小: " << sizeof(arr[0]) << endl;cout << "二维数组元素大小: " << sizeof(arr[0][0]) << endl;cout << "二维数组行数: " << sizeof(arr) / sizeof(arr[0]) << endl;cout << "二维数组列数: " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;//地址cout << "二维数组首地址:" << arr << endl;cout << "二维数组第一行地址:" << arr[0] << endl;cout << "二维数组第二行地址:" << arr[1] << endl;cout << "二维数组第一个元素地址:" << &arr[0][0] << endl;cout << "二维数组第二个元素地址:" << &arr[0][1] << endl;system("pause");return 0;
}

二维数组练习

int main() {int scores[3][3] ={{100,100,100},{90,50,100},{60,70,80},};string names[3] = { "张三","李四","王五" };for (int i = 0; i < 3; i++){int sum = 0;for (int j = 0; j < 3; j++){sum += scores[i][j];}cout << names[i] << "同学总成绩为: " << sum << endl;}system("pause");return 0;
}

1.4 函数

返回值类型 函数名 (参数列表)
{函数体语句return表达式
}
//函数定义
int add(int num1, int num2)                //定义函数时括号内为形参
{int a = num1 + num2;return a;
}int main() {int a = 10;int b = 10;
//函数调用int sum = add(a, b);                   //调用函数时括号内为实参cout << "sum = " << sum << endl;system("pause");return 0;
}

值传递

void swap(int num1, int num2)
{cout << "交换前:" << endl;                // 10  20cout << "num1 = " << num1 << endl;cout << "num2 = " << num2 << endl;int temp = num1;num1 = num2;num2 = temp;cout << "交换后:" << endl;                // 20  10cout << "num1 = " << num1 << endl;cout << "num2 = " << num2 << endl;//return ; 不需要返回值,可以不写return
}int main() {int a = 10;int b = 20;swap(a, b);cout << "mian中的 a = " << a << endl;            //值传递形参修饰不了实参,a还是10cout << "mian中的 b = " << b << endl;            //a=10  b=20system("pause");return 0;
}

几种函数形式

//1、 无参无返
void test01()
{//void a = 10; 报错 无参无返类型不可以创建变量,原因无法分配内存cout << "this is test01" << endl;
}//2、 有参无返
void test02(int a)
{cout << "this is test02" << endl;cout << "a = " << a << endl;
}//3、无参有返
int test03()
{cout << "this is test03 " << endl;return 10;
}//4、有参有返
int test04(int a, int b)
{cout << "this is test04 " << endl;int sum = a + b;return sum;
}

函数的声明

int max(int a, int b);     //声明后,函数定义可以写在main函数的后面了
int max(int a, int b);     //声明可以多次,定义只能一次int main() {int a = 100;int b = 200;cout << max(a, b) << endl;system("pause");return 0;
}
//定义函数之后,该函数就可以写在main函数之后了
int max(int a, int b)
{return a > b ? a : b;
}

函数的分文件编写,头文件写声明,源文件写定义

//swap.h文件
#include<iostream>
using namespace std;//实现两个数字交换的函数声明
void swap(int a, int b);
//swap.cpp文件
#include "swap.h"void swap(int a, int b)
{int temp = a;a = b;b = temp;cout << "a = " << a << endl;cout << "b = " << b << endl;
}

main函数文件

#include<iostream>
#include"swap.h"int main() {int a = 100;int b = 200;swap(a, b);system("pause");return 0;
}

1.5 指针变量

指针变量定义语法: 数据类型 * 变量名;

指针变量和普通变量的区别:普通变量存放的是数据,指针变量存放的是地址

指针变量可以通过 * 操作符操作指针变量指向的内存空间,这个过程称为解引用

int main() {int a = 10; //定义整型变量a//指针定义语法: 数据类型 * 变量名 ;int * p;p = &a; //指针变量赋值指针指向变量a的地址,&是取址符//以上两行等价于 int * p=&a;cout << &a << endl; //打印数据a的地址   010FF968cout << p << endl;  //打印指针变量p     010FF968//指针的使用 通过*操作指针变量指向的内存cout << "*p = " << *p << endl;       //10system("pause");return 0;
}//32为系统指针占4字节,64位占8字节

野指针和空指针不是自己申请的内存因此不可访问

//0x1100不是我们定义的地址
int main() {//指针变量p指向内存地址编号为0x1100的空间int * p = (int *)0x1100;//访问野指针报错 cout << *p << endl;int * p1 =NULL;       //空指针,NULL是c++自带的空指针无需定义直接使用cout << *p1 << endl;  //访问空指针报错 system("pause");return 0;
}

const修饰指针

const修饰指针-常量指针,值不可改,指向可改

const修饰常量-指针常量,指向不可改,值可改

const即修饰指针又修饰常量,指向和值都不可改

int main() {int a = 10;int b = 10;//常量指针,const修饰的是常量,指针指向可以改,指针指向的值不可以更改const int * p1 = &a; p1 = &b; //正确//*p1 = 100;  报错//指针常量,const修饰的是指针,指针指向不可以改,指针指向的值可以更改int * const p2 = &a;//p2 = &b; //错误*p2 = 100; //正确//const既修饰指针又修饰常量const int * const p3 = &a;//p3 = &b; //错误//*p3 = 100; //错误system("pause");return 0;
}

指针和数组

int main() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int * p = arr;  //指向数组的指针,没有取地址符&cout << "第一个元素: " << arr[0] << endl;cout << "指针访问第一个元素: " << *p << endl;for (int i = 0; i < 10; i++){//利用指针遍历数组cout << *p << endl;p++;}system("pause");return 0;
}

指针和函数

//值传递
void swap1(int a ,int b)
{int temp = a;a = b; b = temp;
}
//地址传递
void swap2(int * p1, int *p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}int main() {int a = 10;int b = 20;swap1(a, b); // 值传递不会改变实参,a还是10swap2(&a, &b); //地址传递会改变实参cout << "a = " << a << endl;     //a通过地址传递变20了cout << "b = " << b << endl;system("pause");return 0;
}

指针 函数 数组 组合例子

//冒泡排序函数
void bubbleSort(int * arr, int len)  //int * arr 也可以写为int arr[]
{for (int i = 0; i < len - 1; i++){for (int j = 0; j < len - 1 - i; 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;}
}int main() {int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };int len = sizeof(arr) / sizeof(int);bubbleSort(arr, len);printArray(arr, len);system("pause");return 0;
}

1.6 结构体

//定义结构体时struct不可省略,创建时可以
//结构体定义
struct student
{//成员列表string name;  //姓名int age;      //年龄int score;    //分数
}stu3; //结构体变量创建方式3,定义的时候就创建一个结构体变量。定义结构体需要加分号;int main() {//结构体变量创建方式1struct student stu1; //struct 关键字可以省略stu1.name = "张三";stu1.age = 18;stu1.score = 100;cout << "姓名:" << stu1.name << " 年龄:" << stu1.age  << " 分数:" << stu1.score << endl;//结构体变量创建方式2struct student stu2 = { "李四",19,60 };cout << "姓名:" << stu2.name << " 年龄:" << stu2.age  << " 分数:" << stu2.score << endl;stu3.name = "王五";stu3.age = 18;stu3.score = 80;   cout << "姓名:" << stu3.name << " 年龄:" << stu3.age  << " 分数:" << stu3.score << endl;system("pause");return 0;
}

结构体数组

//结构体定义
struct student
{//成员列表string name;  //姓名int age;      //年龄int score;    //分数
};int main() {//结构体数组struct student arr[3]={{"张三",18,80 },{"李四",19,60 },{"王五",20,70 }};for (int i = 0; i < 3; i++){cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << " 分数:" << arr[i].score << endl;}system("pause");return 0;
}

结构体指针

//结构体定义
struct student
{//成员列表string name;  //姓名int age;      //年龄int score;    //分数
};int main() {struct student stu = { "张三",18,100, };struct student * p = &stu;p->score = 80; //指针通过 -> 操作符可以访问成员cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl;system("pause");return 0;
}

结构体嵌套结构体

//先定义内层的结构体
//学生结构体定义,先定义里层的结构体
struct student
{//成员列表string name;  //姓名int age;      //年龄int score;    //分数
};//教师结构体定义
struct teacher
{//成员列表int id; //职工编号string name;  //教师姓名int age;   //教师年龄struct student stu; //子结构体 学生
};int main() {struct teacher t1;t1.id = 10000;t1.name = "老王";t1.age = 40;t1.stu.name = "张三";t1.stu.age = 18;t1.stu.score = 100;cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl;cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl;system("pause");return 0;
}

结构体做函数参数

//学生结构体定义
struct student
{//成员列表string name;  //姓名int age;      //年龄int score;    //分数
};//值传递
void printStudent(student stu )
{stu.age = 28;cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;
}void printStudent2(student *stu){stu->age = 28;cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age  << " 分数:" << stu->score << endl;
}int main() {student stu = { "张三",18,100};//值传递                 //还是18printStudent(stu);cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;cout << endl;//地址传递               //变28了printStudent2(&stu);cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;system("pause");return 0;
}

结构体中的const

//学生结构体定义
struct student
{//成员列表string name;  //姓名int age;      //年龄int score;    //分数
};//const使用场景
void printStudent(const student *stu) //加const防止函数体中的误操作
{//stu->age = 100; //操作失败,因为加了const修饰cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;}int main() {student stu = { "张三",18,100 };printStudent(&stu);system("pause");return 0;
}

二、第二部分c++核心

2.1 内存分区

2.1.1 内存分区模型

C++程序在执行时,将内存划分为4个区域,不同区域存放的数据赋予不同的生命周期;

代码区:存放函数体的二进制代码,由操作系统进行管理

全局区:存放全局变量和静态变量以及常量

栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等

堆区:由程序员分配和释放,若程序员不释放程序结束时由操作系统自动释放

2.1.2 程序运行前分区

代码区:存放 CPU 执行的机器指令,代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可;代码区是只读的,使其只读的原因是防止程序意外地修改了指令;

全局区:存放全局变量和静态变量;全局区还包含了常量区, 字符串常量和其他常量;该区域的数据在程序结束后由操作系统自动释放;

//全局变量
int g_a = 10;
int g_b = 10;//全局常量
const int c_g_a = 10;
const int c_g_b = 10;int main() {//局部变量int a = 10;int b = 10;//打印地址cout << "局部变量a地址为: " << (int)&a << endl;cout << "局部变量b地址为: " << (int)&b << endl;cout << "全局变量g_a地址为: " <<  (int)&g_a << endl;cout << "全局变量g_b地址为: " <<  (int)&g_b << endl;//静态变量static int s_a = 10;static int s_b = 10;cout << "静态变量s_a地址为: " << (int)&s_a << endl;cout << "静态变量s_b地址为: " << (int)&s_b << endl;cout << "字符串常量地址为: " << (int)&"hello world" << endl;cout << "字符串常量地址为: " << (int)&"hello world1" << endl;cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;const int c_l_a = 10;const int c_l_b = 10;cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl;system("pause");return 0;
}

2.1.3 程序运行后分区

栈区:由编译器自动分配释放;存放函数的参数值、局部变量等;

注意事项:不要返回局部变量的地址;栈区开辟的数据由编译器自动释放;

int * func()
{int a = 10;return &a;
}int main() {int *p = func();cout << *p << endl;   //10cout << *p << endl;  //264616928,第一次跟第二次输出不同,局部变量编译器只保留了一次system("pause");return 0;
}

堆区:由程序员分配释放若程序员不释放;程序结束时由操作系统自动释放

C++主要利用new在堆区开辟内存

int* func()
{int* a = new int(10);        //new 类型();return a;
}int main() {int *p = func();cout << *p << endl;          //10cout << *p << endl;          //10system("pause");return 0;
}

关键字new的用法

int* func()
{int* a = new int(10);return a;
}int main() {int *p = func();cout << *p << endl;cout << *p << endl;delete p;             //利用delete释放堆区数据//cout << *p << endl; 报错,释放的空间不可访问int* arr = new int[10];//堆区开辟数组delete[] arr;          //释放数组delete后多加一个[]system("pause");return 0;
}

2.2 引用

语法: 数据类型 &别名 = 原名

作用:给变量起别名,引用必须初始化,且初始化后不可改变

int main() {int a = 10;int b = 20;//int &c;          错误,引用必须初始化int &c = a;        //初始化c = b;             //这是赋值操作,不是更改引用,改引用是int &c =b;cout << "a = " << a << endl;    //20cout << "b = " << b << endl;    //20cout << "c = " << c << endl;    //20system("pause");return 0;
}

2.2.1 引用做函数参数

引用作函数参数时,形参可以修饰实参 ,引用可以看作简化指针

//1. 值传递
void mySwap01(int a, int b) {int temp = a;a = b;b = temp;
}//2. 地址传递
void mySwap02(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}//3. 引用传递
void mySwap03(int& a, int& b) {int temp = a;a = b;b = temp;
}int main() {int a = 10;int b = 20;mySwap01(a, b);cout << "a:" << a << " b:" << b << endl;     //a=10 b=20mySwap02(&a, &b);cout << "a:" << a << " b:" << b << endl;     //a=20 b=10mySwap03(a, b);cout << "a:" << a << " b:" << b << endl;     //a=20 b=10system("pause");return 0;
}

2.2.2 引用做函数返回值

//返回局部变量引用
int& test01() {int a = 10; //局部变量return a;
}//返回静态变量引用
int& test02() {static int a = 20;return a;
}int main() {//不能返回局部变量的引用int& ref = test01();cout << "ref = " << ref << endl;     //10cout << "ref = " << ref << endl;     //不是10//如果函数做左值,那么必须返回引用int& ref2 = test02();cout << "ref2 = " << ref2 << endl;   //20cout << "ref2 = " << ref2 << endl;   //20test02() = 1000;                     //函数做为左值可以重新赋值cout << "ref2 = " << ref2 << endl;   //1000cout << "ref2 = " << ref2 << endl;   //1000system("pause");return 0;
}

2.2.3 引用本质

//引用本质是指针常量 int &ref=a;自动转换为 int* const ref = &a;
void func(int& ref){ref = 100;      // ref是引用,转换为*ref = 100
}
int main(){int a = 10;int& ref = a;  //自动转换为 int* const ref = &a; //指针常量是指针指向不可改,也说明为什么引用不可更改ref = 20;      //内部发现ref是引用,自动转换为: *ref = 20;cout << "a:" << a << endl;cout << "ref:" << ref << endl;func(a);return 0;
}

2.2.4 常量引用

在函数形参列表中,可以加const修饰形参,防止形参改变实参

//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {//v += 10;            错误,常量引用不可修改cout << v << endl;
}int main() {//int& ref = 10;  引用本身需要一个合法的内存空间,因此这行错误//加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;const int& ref = 10;//ref = 100;  //加入const后不可以修改变量cout << ref << endl;//函数中利用常量引用防止误操作修改实参int a = 10;showValue(a);system("pause");return 0;
}

2.3 函数提高

2.3.1 函数默认参数

int func(int a, int b = 10, int c = 10) {return a + b + c;
}//如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值//如果函数声明有默认值,那么函数实现的时候就不能有默认参数
int func2(int a = 10, int b = 10);
int func2(int a, int b) {return a + b;
}int main() {cout << "ret = " << func(20, 20) << endl;        //50cout << "ret = " << func(100) << endl;           //120cout << "ret = " << func2(20, 20) << endl;       //40system("pause");return 0;
}

2.3.2 函数占位参数

C++中函数的形参列表里可以有占位参数,用来占位,调用函数时必须填补该位置参数

//函数占位参数 ,占位参数也可以有默认参数
void func(int a, int ,b=10) {            //int类型占位,b是默认参数cout << "this is func" << endl;
}int main() {func(10,10); //占位参数必须填补system("pause");return 0;
}

2.3.3 函数重载

函数重载:函数名字可以相同,提高复用性

重载条件:1、同一作用域;2、函数名字相同;3、参数类型不同或个数不同或顺序不同

//函数重载需要函数都在同一个作用域下
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 func(double a, int b)
//{
//  cout << "func (double a ,int b)的调用!" << endl;
//}int main() {func();func(10);func(3.14);func(10,3.14);func(3.14 , 10);system("pause");return 0;
}
//函数重载注意事项
//1、引用作为重载条件void func(int &a)
{cout << "func (int &a) 调用 " << endl;
}void func(const int &a)
{cout << "func (const int &a) 调用 " << endl;
}//2、函数重载碰到函数默认参数void func2(int a, int b = 10)
{cout << "func2(int a, int b = 10) 调用" << endl;
}void func2(int a)
{cout << "func2(int a) 调用" << endl;
}int main() {int a = 10;func(a); //调用无constfunc(10);//调用有const//func2(10); //碰到默认参数产生歧义,需要避免system("pause");return 0;
}

2.4 类和对象

c++面向对象的3大特性:封装、继承、多态

​具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类
人可以作为对象,属性有姓名、年龄、身高、体重…,行为有走、跑、吃饭…
车可以作为对象,属性有颜色、品牌、尺寸、价格…,行为有跑、放音乐、放空调…

2.4.1 封装

语法: class 类名{ 访问权限: 属性 / 行为 };

封装的意义:将属性和行为作为一个整体,用来表现生活中的事物

封装圆类示例:

//圆周率
const double PI = 3.14;//封装一个圆类,求圆的周长
//class代表设计一个类,后面跟着的是类名
class Circle
{
public:                      //访问权限  公共的权限//属性int m_r;//半径//行为double calculateZC()      //获取到圆的周长{//获取圆的周长 2 * pi  * rreturn  2 * PI * m_r;}
};int main() {Circle c1;                //通过圆类,创建圆的对象 c1就是一个具体的圆c1.m_r = 10;            //给圆对象的半径 进行赋值操作cout << "圆的周长为: " << c1.calculateZC() << endl;system("pause");return 0;
}

封装学生类示例:设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号

//学生类
class Student {
public:void setName(string name) {m_name = name;}void setID(int id) {m_id = id;}void showStudent() {cout << "name:" << m_name << " ID:" << m_id << endl;}
public:string m_name;int m_id;
};int main() {Student stu;               //创建一个具体的学生stu.setName("德玛");stu.setID(250);stu.showStudent();system("pause");return 0;
}

类在封装时,可以把属性和行为赋予不同的权限;

1公共权限 public 类内可以访问 类外可以访问

2保护权限 protected 类内可以访问 类外不可以访问

3私有权限 private 类内可以访问 类外不可以访问(看着与保护权限相同,但是保护权限在继承的类中可以访问,私有的不可以访问,仅类内可以访问)

class C1
{int  m_A; //默认是私有权限
};struct C2
{int m_A;  //默认是公共权限
};int main() {C1 c1;c1.m_A = 10; //错误,访问权限是私有C2 c2;c2.m_A = 10; //正确,访问权限是公共system("pause");return 0;
}

struct默认公共权限,类默认私有,私有权限的好处是控制读和写,检测写入数据的有效性

class Person {
public:void setName(string name) {              //姓名设置可读可写m_Name = name; }string getName(){return m_Name;}//获取年龄 int getAge() {return m_Age;}//设置年龄void setAge(int age) {if (age < 0 || age > 150) {cout << "你个老妖精!" << endl;return;}m_Age = age;}//情人设置为只写void setLover(string lover) {m_Lover = lover;}private:string m_Name; //可读可写  姓名int m_Age; //只读  年龄string m_Lover; //只写  情人
};int main() {Person p;//姓名设置p.setName("张三");cout << "姓名: " << p.getName() << endl;//年龄设置p.setAge(50);cout << "年龄: " << p.getAge() << endl;//情人设置p.setLover("苍井");//cout << "情人: " << p.m_Lover << endl;   错误,只写属性,不可以读取system("pause");return 0;
}

2.4.2 对象的初始化和清理

c++利用了构造函数和析构函数完成对象初始化和清理工作,这两个函数将会被编译器自动调用,如果写了这两个函数编译器就调用已经写的,如果不写就调用两个空实现函数

构造函数语法: 类名 (){}

析构函数语法: ~类名 (){}

class Person
{
public://构造函数Person(){cout << "Person的构造函数调用" << endl;}//析构函数~Person(){cout << "Person的析构函数调用" << endl;}};void test01()
{Person p;
}int main() {test01();system("pause");return 0;
}

构造函数两种分类:1有参无参;2普通构造和拷贝构造;

//1、构造函数分类
// 按照参数分类分为 有参和无参构造 无参又称为默认构造函数
// 按照类型分类分为 普通构造和拷贝构造class Person {
public:Person() {                               //无参(默认)构造函数cout << "无参构造函数!" << endl;}Person(int a) {                         //有参构造函数age = a;cout << "有参构造函数!" << endl;}Person(const Person& p) {               //拷贝构造函数age = p.age;cout << "拷贝构造函数!" << endl;}//析构函数~Person() {cout << "析构函数!" << endl;}
public:int age;
};void test01() {Person p; //调用无参构造函数
}void test02() {Person p1(10);                         //调用有参的构造函数(1括号法,常用)//调用无参构造函数不能加括号,否则认为这是一个函数声明
//Person(10)单独写就是匿名对象  当前行结束之后,马上析构Person p2 = Person(10);                //调用有参的构造函数(2显式法)Person p3 = Person(p2);Person p4 = 10; // Person p4 = Person(10);//调用有参的构造函数(隐式转换法)Person p5 = p4; // Person p5 = Person(p4);//调用有参的构造函数(隐式转换法)}int main() {test01();test02();system("pause");return 0;
}

拷贝构造的时机:1用已经创建的对象初始化新对象;2值传递方式给函数参数传值;3以值返回局部对象

class Person {
public:Person() {cout << "无参构造函数!" << endl;mAge = 0;}Person(int age) {cout << "有参构造函数!" << endl;mAge = age;}Person(const Person& p) {cout << "拷贝构造函数!" << endl;mAge = p.mAge;}//析构函数在释放内存之前调用~Person() {cout << "析构函数!" << endl;}
public:int mAge;
};//1使用一个已经创建完毕的对象来初始化一个新对象
void test01() {Person man(100); //p对象已经创建完毕Person newman(man); //调用拷贝构造函数,使用一个已经创建完毕的对象来初始化一个新对象Person newman2 = man; //拷贝构造,使用一个已经创建完毕的对象来初始化一个新对象//Person newman3;    //newman3 = man; 不是调用拷贝构造函数,是赋值操作
}//2值传递的方式给函数参数传值;
void doWork(Person p1) {}
void test02() {Person p; //无参构造函数doWork(p);
}//3以值方式返回局部对象
Person doWork2()
{Person p1;cout << (int *)&p1 << endl;return p1;
}void test03()
{Person p = doWork2();       //p具有和p1相同的属性和行为cout << (int *)&p << endl;
}int main() {//test01();//test02();test03();system("pause");return 0;
}

深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间进行拷贝操作

注意事项:如果属性有在堆区开辟的一定要自己提供拷贝构造函数防止浅拷贝带来问题

class Person {
public:Person() {                               //无参(默认)构造函数cout << "无参构造函数!" << endl;}Person(int age ,int height) {            //有参构造函数cout << "有参构造函数!" << endl;m_age = age;m_height = new int(height);          //有堆区数据,拷贝需要深拷贝}Person(const Person& p) {                //拷贝构造函数cout << "拷贝构造函数!" << endl;//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题m_age = p.m_age;m_height = new int(*p.m_height);}//析构函数~Person() {cout << "析构函数!" << endl;if (m_height != NULL){delete m_height;}}
public:int m_age;int* m_height;
};void test01()
{Person p1(18, 180);Person p2(p1);cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl; //18 180cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl; //18 180
}int main() {test01();system("pause");return 0;
}

2.4.3 初始化列表

class Person {
public://传统方式初始化//Person(int a, int b, int c) {//   m_A = a;// m_B = b;// m_C = c;//}//初始化列表方式初始化Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}void PrintPerson() {cout << "mA:" << m_A << endl;cout << "mB:" << m_B << endl;cout << "mC:" << m_C << endl;}
private:int m_A;int m_B;int m_C;
};int main() {Person p(1, 2, 3);p.PrintPerson();system("pause");return 0;
}

类对象作为类成员

class A {}
class B
{A a;
}
class Phone
{
public:Phone(string name){m_PhoneName = name;cout << "Phone构造" << endl;}~Phone(){cout << "Phone析构" << endl;}string m_PhoneName;};class Person
{
public://初始化列表可以告诉编译器调用哪一个构造函数Person(string name, string pName) :m_Name(name), m_Phone(pName){cout << "Person构造" << endl;}~Person(){cout << "Person析构" << endl;}void playGame(){cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl;}string m_Name;Phone m_Phone;              //类对象做为类成员};
void test01()
{//当类中成员是其他类对象时,我们称该成员为对象成员//构造的顺序是:先调用对象成员的构造再调用本类构造//析构顺序与构造相反Person p("张三" , "苹果X");p.playGame();}int main() {test01();system("pause");return 0;
}

2.4.4 静态成员

在成员变量和成员函数前加上关键字static,称为静态成员

静态成员变量特点:1所有对象共享同一份数据;2在编译阶段分配内存;3类内声明类外初始化

class Person
{public:static int m_A;         //静态成员变量private:static int m_B;
};
int Person::m_A = 10;
int Person::m_B = 10;       //作用域的方式“类外”写私有权限的m_B,不是静态成员不可以void test01()
{//静态成员变量两种访问方式//1通过对象Person p1;p1.m_A = 100;cout << "p1.m_A = " << p1.m_A << endl; //100Person p2;p2.m_A = 200;cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据,现在是200cout << "p2.m_A = " << p2.m_A << endl; //200//2、通过类名cout << "m_A = " << Person::m_A << endl;//200//cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
}int main() {test01();system("pause");return 0;
}

静态成员函数特点:1所有对象共享同一个函数;2静态成员函数只能访问静态成员变量

class Person
{public:static void func(){cout << "func调用" << endl;m_A = 100;//m_B = 100; //错误,不可以访问非静态成员变量}static int m_A; //静态成员变量int m_B; //
private:static void func2(){cout << "func2调用" << endl;}
};
int Person::m_A = 10;void test01()
{//静态成员函数两种访问方式//通过对象Person p1;p1.func();//2通过类名Person::func();//Person::func2(); //私有权限访问不到
}int main() {test01();system("pause");return 0;
}

2.4.5 c++对象模型和this指针

C++类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象

class Person {
public:Person() {mA = 0;}//非静态成员变量占对象空间int mA;//静态成员变量不占对象空间static int mB; //函数也不占对象空间,所有函数共享一个函数实例,多个同类型的对象会共用一块代码void func() {cout << "mA:" << this->mA << endl;}//静态成员函数也不占对象空间static void sfunc() {}
};int main() {cout << sizeof(Person) << endl;system("pause");return 0;
}

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码;

Person类可以实例化出p1、p2、p3众多对象,代码只有一份怎么知道是p1调用的还是p2调用的?

这一块代码是如何区分那个对象调用自己的呢?

this指针指向被调用的成员函数所属的对象 ,p1调用就指向p1,p2调用就指向p2

this指针不需要定义,直接使用即可

this指针的用途:1当形参和成员变量同名时用this指针来区分;2在类的非静态成员函数中返回对象本身时可使用return *this

class Person
{
public:Person(int age){//1当形参和成员变量同名时,可用this指针来区分this->age = age;}Person& PersonAddPerson(Person p){this->age += p.age;//2返回对象本身return *this;}int age;
};void test01()
{Person p1(10);cout << "p1.age = " << p1.age << endl;Person p2(10);p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);  //40cout << "p2.age = " << p2.age << endl;
}int main() {test01();system("pause");return 0;
}

空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是要注意有没有用到this指针如果用到this指针,需要加以判断保证代码的健壮性

//空指针访问成员函数
class Person {
public:void ShowClassName() {cout << "我是Person类!" << endl;}void ShowPerson() {if (this == NULL) {return;}cout << mAge << endl;   }public:int mAge;
};void test01()
{Person * p = NULL;p->ShowClassName(); //空指针,可以调用成员函数p->ShowPerson();  //但是如果成员函数中用到了this指针,就不可以了
}                      //没有输出mAge,this为空返回了,没有输出mAgeint main() {test01();system("pause");return 0;
}

2.4.6 const修饰成员函数

常函数:1成员函数后加const后我们称为这个函数为常函数;2常函数内不可以修改成员属性;3成员属性声明时加关键字mutable后在常函数中可以修改

常对象:1声明对象前加const称该对象为常对象;2常对象只能调用常函数

class Person {
public:Person() {m_A = 0;m_B = 0;}//this指针的本质是一个指针常量,指针的指向不可修改//如果想让指针指向的值也不可以修改,需要声明常函数void ShowPerson() const {//this = NULL; //不能修改指针的指向 Person* const this;//this->m_A = 100; //但是this指针指向的对象的数据是可以修改的//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量this->m_B = 100;}void MyFunc() const {//m_A = 10000;}public:int m_A;mutable int m_B; //可修改 可变的
};//const修饰对象  常对象
void test01() {const Person person; //常对象,只能调用常函数  cout << person.m_A << endl;//person.m_A = 100; //常对象不能修改成员变量的值,但是可以访问person.m_B = 100; //但是常对象可以修改mutable修饰成员变量//常对象访问成员函数person.MyFunc(); //常对象只能调用后面加const的常函数}int main() {test01();system("pause");return 0;
}

2.4.7 友元

友元的目的就是让一个函数或者类访问另一个类中私有成员;友元的关键字为 friend

友元的三种实现:1全局函数做友元;2类做友元;3成员函数做友元

全局函数做友元

class Building
{//告诉编译器 全局函数 goodGay是 Building类的好朋友,可以访问类中的私有内容friend void goodGay(Building * building);public:Building(){this->m_SittingRoom = "客厅";this->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 b;goodGay(&b);
}int main(){test01();system("pause");return 0;
}

类做友元

class Building;          //先告诉编译器有个Building类
class goodGay
{
public:goodGay();void visit();private:Building *building;  //指针!!!
};class Building
{//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容friend class goodGay;public:Building();public:string m_SittingRoom; //客厅
private:string m_BedRoom;//卧室
};Building::Building()      //成员函数类外实现(写私有变量),私有成员变量不行需要加static
{this->m_SittingRoom = "客厅";this->m_BedRoom = "卧室";
}goodGay::goodGay()        //成员函数类外实现需要类内声明 goodGay();
{building = new Building;     //building是指针,堆区创建一个Building返回给他
}void goodGay::visit()
{cout << "好基友正在访问" << building->m_SittingRoom << endl;cout << "好基友正在访问" << building->m_BedRoom << endl;
}void test01()
{goodGay gg;gg.visit();}int main(){test01();system("pause");return 0;
}

成员函数做友元


class Building;
class goodGay
{
public:goodGay();void visit(); //只让visit函数作为Building的好朋友,可以发访问Building中私有内容void visit2(); //visit2不可以访问private:Building *building;  //友元的指针!!!
};class Building
{//告诉编译器  goodGay类中的visit成员函数 是Building好朋友,可以访问私有内容friend void goodGay::visit();public:Building();public:string m_SittingRoom; //客厅
private:string m_BedRoom;//卧室
};Building::Building()
{this->m_SittingRoom = "客厅";this->m_BedRoom = "卧室";
}goodGay::goodGay()
{building = new Building;
}void goodGay::visit()
{cout << "好基友正在访问" << building->m_SittingRoom << endl;cout << "好基友正在访问" << building->m_BedRoom << endl;
}void goodGay::visit2()
{cout << "好基友正在访问" << building->m_SittingRoom << endl;//cout << "好基友正在访问" << building->m_BedRoom << endl;
}void test01()
{goodGay  gg;gg.visit();}int main(){test01();system("pause");return 0;
}

2.4.8 运算符重载

运算符重载:对已有的运算符重新进行定义,以适应自定义数据类型运算

加号运算符重载

class Person {
public:Person() {};Person(int a, int b){this->m_A = a;this->m_B = b;}//成员函数实现 + 号运算符重载Person operator+(const Person& p) {Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}public:int m_A;int m_B;
};//全局函数实现 + 号运算符重载
//Person operator+(const Person& p1, const Person& p2) {
//  Person temp(0, 0);
//  temp.m_A = p1.m_A + p2.m_A;
//  temp.m_B = p1.m_B + p2.m_B;
//  return temp;
//}//运算符重载 可以发生函数重载
Person operator+(const Person& p2, int val)
{Person temp;temp.m_A = p2.m_A + val;temp.m_B = p2.m_B + val;return temp;
}void test() {Person p1(10, 10);Person p2(20, 20);//成员函数方式Person p3 = p2 + p1;  //相当于 p2.operator+(p1)cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;Person p4 = p3 + 10; //相当于 operator+(p3,10)cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl;}int main() {test();system("pause");return 0;
}

左移运算符重载

class Person {friend ostream& operator<<(ostream& out, Person& p);    //全局函数做友元public:Person(int a, int b){this->m_A = a;this->m_B = b;}private:int m_A;int m_B;
};//全局函数实现左移重载
ostream& operator<<(ostream& out, Person& p) { //ostream对象只能有一个所以用引用的方式outout << "a:" << p.m_A << " b:" << p.m_B;return out;
}void test() {Person p1(10, 20);cout << p1 << "hello world" << endl;
//链式编程,如果是void operator<<(ostream& out, Person& p),就不能追加endl了
//用cout接收返回值,cout属于ostream类,用ostream&
}int main() {test();system("pause");return 0;
}

递增运算符重载


class MyInteger {friend ostream& operator<<(ostream& out, MyInteger myint);  //友元public:MyInteger() {m_Num = 0;}MyInteger& operator++() {      //前置++m_Num++;                    //先++       return *this;               //再返回}MyInteger operator++(int) {     //后置++//先返回MyInteger temp = *this;
//记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;m_Num++;return temp;}private:int m_Num;
};ostream& operator<<(ostream& out, MyInteger myint) {      out << myint.m_Num;           //左移运算符重载return out;
}void test01() {                   //前置++ 先++ 再返回MyInteger myInt;cout << ++myInt << endl;cout << myInt << endl;
}void test02() {                   //后置++ 先返回 再++MyInteger myInt;cout << myInt++ << endl;cout << myInt << endl;
}int main() {test01();//test02();system("pause");return 0;
}

赋值运算符重载

class Person
{
public:Person(int age){m_Age = new int(age);             //将年龄数据开辟到堆区//指针m_Age接收}Person& operator=(Person &p)          //重载赋值运算符 {if (m_Age != NULL){delete m_Age;m_Age = NULL;}//m_Age = p.m_Age;                编译器提供的代码是浅拷贝m_Age = new int(*p.m_Age);        //提供深拷贝 解决浅拷贝的问题return *this;                     //返回自身}~Person(){if (m_Age != NULL){delete m_Age;m_Age = NULL;}}int *m_Age;                           //年龄的指针
};void test01()
{Person p1(18);Person p2(20);Person p3(30);p3 = p2 = p1;                         //赋值操作cout << "p1的年龄为:" << *p1.m_Age << endl;   //解引用cout << "p2的年龄为:" << *p2.m_Age << endl;cout << "p3的年龄为:" << *p3.m_Age << endl;
}int main() {test01();system("pause");return 0;
}

关系运算符重载

class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;};bool operator==(Person & p){if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return true;}else{return false;}}bool operator!=(Person & p){if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return false;}else{return true;}}string m_Name;int m_Age;
};void test01()
{Person a("孙悟空", 18);Person b("孙悟空", 18);if (a == b){cout << "a和b相等" << endl;}else{cout << "a和b不相等" << endl;}if (a != b){cout << "a和b不相等" << endl;}else{cout << "a和b相等" << endl;}
}int main() {test01();system("pause");return 0;
}

调用函数运算符重载

class MyPrint
{
public:void operator()(string text){cout << text << endl;}};
void test01()
{MyPrint myFunc;                   //创建类 myFunc("hello world");            //重载的()操作符 也称为仿函数
}class MyAdd
{
public:int operator()(int v1, int v2){return v1 + v2;}
};void test02()
{MyAdd add;int ret = add(10, 10);              //放实参的括号()重载为仿函数cout << "ret = " << ret << endl;    //20cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;//200,匿名对象调用:类名() ,匿名对象调完就释放
}int main() {test01();test02();system("pause");return 0;
}

2.5 继承

继承的语法:class 子类 : 继承方式 父类         继承方式3种:1公共继承;2保护继承;3私有继承

示例,一个学习网站,java页面和python页面和c++页面很多内容都是重复的

//Java页面
class Java
{
public:void header(){cout << "首页、公开课、登录、注册...(公共头部)" << endl;}void footer(){cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;}void left(){cout << "Java,Python,C++...(公共分类列表)" << endl;}void content(){cout << "JAVA学科视频" << endl;}
};
//Python页面
class Python
{
public:void header(){cout << "首页、公开课、登录、注册...(公共头部)" << endl;}void footer(){cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;}void left(){cout << "Java,Python,C++...(公共分类列表)" << endl;}void content(){cout << "Python学科视频" << endl;}
};
//C++页面
class CPP
{
public:void header(){cout << "首页、公开课、登录、注册...(公共头部)" << endl;}void footer(){cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;}void left(){cout << "Java,Python,C++...(公共分类列表)" << endl;}void content(){cout << "C++学科视频" << endl;}
};void test01()
{//Java页面cout << "Java下载视频页面如下: " << endl;Java ja;ja.header();ja.footer();ja.left();ja.content();cout << "--------------------" << endl;//Python页面cout << "Python下载视频页面如下: " << endl;Python py;py.header();py.footer();py.left();py.content();cout << "--------------------" << endl;//C++页面cout << "C++下载视频页面如下: " << endl;CPP cp;cp.header();cp.footer();cp.left();cp.content();}int main() {test01();system("pause");return 0;
}

类与类之间存在联系,利用继承的技术,减少重复代码

//公共页面
class BasePage
{
public:void header(){cout << "首页、公开课、登录、注册...(公共头部)" << endl;}void footer(){cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;}void left(){cout << "Java,Python,C++...(公共分类列表)" << endl;}};//Java页面
class Java : public BasePage                   //公共继承
{
public:void content(){cout << "JAVA学科视频" << endl;}
};
//Python页面
class Python : public BasePage
{
public:void content(){cout << "Python学科视频" << endl;}
};
//C++页面
class CPP : public BasePage
{
public:void content(){cout << "C++学科视频" << endl;}
};void test01()
{//Java页面cout << "Java下载视频页面如下: " << endl;Java ja;ja.header();ja.footer();ja.left();ja.content();cout << "--------------------" << endl;//Python页面cout << "Python下载视频页面如下: " << endl;Python py;py.header();py.footer();py.left();py.content();cout << "--------------------" << endl;//C++页面cout << "C++下载视频页面如下: " << endl;CPP cp;cp.header();cp.footer();cp.left();cp.content();}int main() {test01();system("pause");return 0;
}

3种继承方式:1公共继承;2保护继承;3私有继承;

父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到

class Base1
{
public: int m_A;
protected:int m_B;
private:int m_C;
};class Son1 :public Base1                 //公共继承
{
public:void func(){m_A;                             //可访问 public权限m_B;                             //可访问 protected权限//m_C;                           不可访问}
};void myClass()
{Son1 s1;s1.m_A;                              //其他类只能访问到公共权限
}class Base2                              //保护继承
{
public:int m_A;
protected:int m_B;
private:int m_C;
};
class Son2:protected Base2
{
public:void func(){m_A;                            //可访问 protected权限m_B;                            //可访问 protected权限//m_C;                          //保护权限类外不可访问}
};
void myClass2()
{Son2 s;//s.m_A;                            //不可访问
}class Base3                             //私有继承
{
public:int m_A;
protected:int m_B;
private:int m_C;
};
class Son3:private Base3
{
public:void func(){m_A;                          //可访问 private权限m_B;                          //可访问 private权限//m_C;                         不可访问}
};
class GrandSon3 :public Son3
{
public:void func(){//Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到//m_A;//m_B;//m_C;}
};

继承关系中,子类构造和析构会调用父类 构造和析构顺序是:先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

继承同名成员处理方式(子类和父类中有同名字的成员)

1访问子类同名成员直接访问即可;2访问父类同名成员需要加作用域;

class Base {
public:Base(){m_A = 100;}void func(){cout << "Base - func()调用" << endl;}void func(int a){cout << "Base - func(int a)调用" << endl;}public:int m_A;
};class Son : public Base {
public:Son(){m_A = 200;}//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域void func(){cout << "Son - func()调用" << endl;}
public:int m_A;
};void test01()
{Son s;cout << "Son下的m_A = " << s.m_A << endl;cout << "Base下的m_A = " << s.Base::m_A << endl;s.func();s.Base::func();s.Base::func(10);}
int main() {test01();system("pause");return EXIT_SUCCESS;    //等同 return 0;
}

继承同名静态成员处理方式与非静态一样,只不过有两种访问方式:1通过对象;2通过类名;

//同名静态成员
void test01()
{//通过对象访问cout << "通过对象访问: " << endl;Son s;cout << "Son  下 m_A = " << s.m_A << endl;cout << "Base 下 m_A = " << s.Base::m_A << endl;//通过类名访问cout << "通过类名访问: " << endl;cout << "Son  下 m_A = " << Son::m_A << endl;cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}

多继承方式,一个类继承多个类,语法:class 子类:继承方式 父类1 ,继承方式 父类2

多继承容易同名不建议使用( 多继承中如果父类中出现了同名情况,子类使用时候要加作用域)

class Son : public Base2, public Base1 ,protected Base3
{...
}

菱形继承:两个派生类继承同一个基类,同时一个类继承这两个派生类

羊继承了动物的数据,驼同样继承了动物的数据,当羊驼继承羊和驼使用数据时就会产生二义性

class Animal
{
public:int m_Age;
};//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo   : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};void test01()
{SheepTuo st;st.Sheep::m_Age = 100;st.Tuo::m_Age = 200;                 //假如这行注释掉,st.m_Age就是100cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;    //100cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;       //200cout << "st.m_Age = " << st.m_Age << endl;                  //200
}int main() {test01();system("pause");return 0;
}

2.6 多态

多态分为两类

静态多态: 函数重载和运算符重载复用函数名属于静态多态,静态多态函数地址早绑定 - 编译阶段确定函数地址

动态多态: 派生类(子类)和虚函数实现运行时多态,动态多态的函数地址晚绑定 - 运行阶段确定函数地址

多态条件: 1有继承关系;2子类重写父类中的虚函数;多态使用:父类指针或引用指向子类对象

class Animal
{
public://函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了virtual void speak()                 //Speak函数是虚函数{cout << "动物在说话" << endl;}
};class Cat :public Animal
{
public:void speak(){cout << "小猫在说话" << endl;}
};class Dog :public Animal
{
public:void speak(){cout << "小狗在说话" << endl;}};
//我们希望传入什么对象,那么就调用什么对象的函数
//如果函数地址在编译阶段就能确定,那么静态联编
//如果函数地址在运行阶段才能确定,就是动态联编void DoSpeak(Animal & animal)      //父类指针或引用指向子类对象
{animal.speak();
}
//
//多态满足条件:
//1有继承关系
//2子类重写父类中的虚函数
//多态使用:父类指针或引用指向子类对象
void test01()
{Cat cat;DoSpeak(cat);                   //父类指针或引用指向子类对象Dog dog;DoSpeak(dog);
}int main() {test01();system("pause");return 0;
}

多态示例计算器

此处用到new+类名的用法,补充一下new+类名创建类对象知识点
new创建类对象需要指针接收,一处初始化,多处使用
new创建类对象使用完需delete销毁
new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
new对象指针用途广泛,比如作为函数返回值、函数参数等
频繁调用场合并不适合new,就像new申请和释放内存一样

//普通实现
class Calculator {
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;}//如果要提供新的运算,需要此处添加源码}
public:int m_Num1;int m_Num2;
};void test01()
{//普通实现测试Calculator 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()
{//创建加法计算器//new加类名创建出一个类的指针,前面类做友元碰到过这个知识点//父类指针或引用指向子类对象AbstractCalculator *abc = new AddCalculator;abc->m_Num1 = 10;abc->m_Num2 = 10;cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;delete abc;               //new创建的,用完了记得销毁//创建减法计算器abc = new SubCalculator;  //new创建对象,一次初始化多次使用//为什么不是AbstractCalculator *abc = new SubCalculator?abc->m_Num1 = 10;abc->m_Num2 = 10;cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;delete abc;  //创建乘法计算器abc = new MulCalculator;abc->m_Num1 = 10;abc->m_Num2 = 10;cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;delete abc;
}int main() {//test01();test02();system("pause");return 0;
}

在多态中通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容因此可以将虚函数改为纯虚函数;纯虚函数特点:1无法实例化对象;2子类必须重写纯虚函数否则也属于抽象类

纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ; 当类中有纯虚函数这个类称为抽象类

class Base
{
public://纯虚函数//类中只要有一个纯虚函数就称为抽象类//抽象类无法实例化对象//子类必须重写父类中的纯虚函数,否则也属于抽象类virtual void func() = 0;
};class Son :public Base
{
public:virtual void func() {cout << "func调用" << endl;};
};void test01()
{Base * base = NULL;//base = new Base;                错误,抽象类无法实例化对象base = new Son;base->func();delete base;                      //记得销毁
}int main() {test01();system("pause");return 0;
}

多态示例2 制作饮品

//抽象制作饮品
class AbstractDrinking {
public://烧水virtual void Boil() = 0;//冲泡virtual void Brew() = 0;//倒入杯中virtual void PourInCup() = 0;//加入辅料virtual void PutSomething() = 0;//规定流程void MakeDrink() {Boil();Brew();PourInCup();PutSomething();}
};//制作咖啡
class Coffee : public AbstractDrinking {
public://烧水virtual void Boil() {cout << "煮农夫山泉!" << endl;}//冲泡virtual void Brew() {cout << "冲泡咖啡!" << endl;}//倒入杯中virtual void PourInCup() {cout << "将咖啡倒入杯中!" << endl;}//加入辅料virtual void PutSomething() {cout << "加入牛奶!" << endl;}
};//制作茶水
class Tea : public AbstractDrinking {
public://烧水virtual void Boil() {cout << "煮自来水!" << endl;}//冲泡virtual void Brew() {cout << "冲泡茶叶!" << endl;}//倒入杯中virtual void PourInCup() {cout << "将茶水倒入杯中!" << endl;}//加入辅料virtual void PutSomething() {cout << "加入枸杞!" << endl;}
};//业务函数
void DoWork(AbstractDrinking* drink) {drink->MakeDrink();       delete drink;
}void test01() {DoWork(new Coffee);        DoWork(new Tea);
}int main() {test01();system("pause");return 0;
}

多态使用时如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码;

解决方式:将父类中的析构函数改为虚析构virtual ~类名(){}或者纯虚析构virtual ~类名() = 0;

如果子类中没有堆区数据可以不写为虚析构或纯虚析构;拥有纯虚析构的类属于抽象类无法实例

class Animal {
public:Animal(){cout << "Animal 构造函数调用!" << endl;}virtual void Speak() = 0;//virtual ~Animal()                  析构函数加上virtual关键字,变成虚析构函数//{//  cout << "Animal虚析构函数调用!" << endl;//}
//父类可能也有堆区的数据,所以虚析构和纯虚析构必须要有具体实现,具体实现就是花括号{}virtual ~Animal() = 0;              //纯虚析构函数
};Animal::~Animal()         //纯虚析构函数的具体实现(上面定义了是virtual此处不用virtual)
{cout << "Animal 纯虚析构函数调用!" << endl;
}//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。class Cat : public Animal {
public:Cat(string name){cout << "Cat构造函数调用!" << endl;m_Name = new string(name);}virtual void Speak(){cout << *m_Name <<  "小猫在说话!" << endl;}~Cat(){cout << "Cat析构函数调用!" << endl;if (this->m_Name != NULL) {delete m_Name;m_Name = NULL;}}public:string *m_Name;
};void test01()
{Animal *animal = new Cat("Tom");animal->Speak();//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏//解决方法给基类增加一个虚析构函数//虚析构函数就是用来解决通过父类指针释放子类对象delete animal;
}int main() {test01();system("pause");return 0;
}

多态示例3

//示例描述:不同厂商的电脑零件组装成不同的电脑
#include<iostream>
using namespace std;//抽象CPU类
class CPU
{
public://抽象的计算函数virtual void calculate() = 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->calculate();m_vc->display();m_mem->storage();}//提供析构函数 释放3个零件~Computer(){//释放CPU零件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;          //CPU的零件指针VideoCard * m_vc;    //显卡零件指针Memory * m_mem;      //内存条零件指针
};
//具体厂商
//Intel厂商
class IntelCPU :public CPU
{
public:virtual void calculate(){cout << "Intel的CPU开始计算了!" << endl;}
};
class IntelVideoCard :public VideoCard
{
public:virtual void display(){cout << "Intel的显卡开始显示了!" << endl;}
};
class IntelMemory :public Memory
{
public:virtual void storage(){cout << "Intel的内存条开始存储了!" << endl;}
};
//Lenovo厂商
class LenovoCPU :public CPU
{
public:virtual void calculate(){cout << "Lenovo的CPU开始计算了!" << endl;}
};
class LenovoVideoCard :public VideoCard
{
public:virtual void display(){cout << "Lenovo的显卡开始显示了!" << endl;}
};
class LenovoMemory :public Memory
{
public:virtual void storage(){cout << "Lenovo的内存条开始存储了!" << endl;}
};void test01()
{//首台电脑零件CPU * intelCpu = new IntelCPU;              //父类指针指向子类,多态VideoCard * intelCard = new IntelVideoCard; //父类指针指向子类,多态Memory * intelMem = new IntelMemory;        //父类指针指向子类,多态cout << "第一台电脑开始工作:" << endl;//创建第一台电脑Computer * computer1 = new Computer(intelCpu, intelCard, intelMem);computer1->work();delete computer1;cout << "-----------------------" << endl;cout << "第二台电脑开始工作:" << endl;//第二台电脑组装Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);computer2->work();delete computer2;cout << "-----------------------" << endl;cout << "第三台电脑开始工作:" << endl;//第三台电脑组装Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);computer3->work();delete computer3;
}
int main() {test01();system("pause");return 0;
}

2.7 文件操作

程序运行时产生的数据都属于临时数据,程序运行结束时会被释放,通过文件可以将数据保存下来

文件类型分为两种:1文本文件以ASCII码形式存储;2 二进制文件以二进制形式存储,不便阅读

操作文件的三大类: 1 ofstream写操作;2 ifstream读操作;3 fstream读写操作

文件打开方式利用|操作符可以配合使用,例如:用二进制方式写文件 ios::binary | ios:: out

写文件示例:

#include <fstream>                    //包含头文件void test01()
{ofstream ofs;                     //创建流对象ofs.open("test.txt", ios::out);   //路径和打开方式ofs << "姓名:张三" << endl;      //写内容ofs << "性别:男" << endl;        //写内容ofs << "年龄:18" << endl;        //写内容ofs.close();                      //关闭
}int main() {test01();system("pause");return 0;
}

读文件示例

#include <fstream>                          //包含头文件
#include <string>
void test01()
{ifstream ifs;                           //创建流ifs.open("test.txt", ios::in);          //路径和打开方式if (!ifs.is_open())                     //is_open()可以判断是否打开{cout << "文件打开失败" << endl;return;}//第一种方式//char buf[1024] = { 0 };             //用一个长为1024字节字符串数组接收读的内容//while (ifs >> buf)//{//   cout << buf << endl;//}//第二种//char buf[1024] = { 0 };//while (ifs.getline(buf,sizeof(buf)))//成员函数getline读取一行,两个参数//{                           //一个是读到的内容放到哪儿(buf),一个是(buf)长度//  cout << buf << endl;      //sizeof(buf)可以改为1024//}//第三种//string buf;                 //要包含string头文件//while (getline(ifs, buf))//{// cout << buf << endl;//}char c;                       //一个字符一个字符的读while ((c = ifs.get()) != EOF)//EOF end of file文件尾{cout << c;}ifs.close();                           //关闭文件}int main() {test01();system("pause");return 0;
}

二进制写文件(二进制写完可能是乱码但可以正常读出来,二进制好处是可以写自定义数据类型)

#include <fstream>
#include <string>class Person
{
public:char m_Name[64];int m_Age;
};//二进制文件  写文件
void test01()
{//1包含头文件//2创建输出流对象ofstream ofs("person.txt", ios::out | ios::binary);//3打开文件//ofs.open("person.txt", ios::out | ios::binary); 这一步可以合并到步骤2,括号复制过去Person p = {"张三"  , 18};//4写文件                         //语法 ostream& write(const char * buffer,int len);ofs.write((const char *)&p, sizeof(p));//ostream& write(const char * buffer,int len);//5关闭文件ofs.close();
}int main() {test01();system("pause");return 0;
}

读二进制文件

#include <fstream>
#include <string>class Person
{
public:char m_Name[64];int m_Age;
};void test01()
{ifstream ifs("person.txt", ios::in | ios::binary);if (!ifs.is_open()){cout << "文件打开失败" << endl;}Person p;ifs.read((char *)&p, sizeof(p));   //语法 istream& read(char *buffer,int len);cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}int main() {test01();system("pause");return 0;
}

三、第三部分c++提高

学习C++泛型编程和STL(Standard Template Library,标准模板库)探讨C++更深层的使用

3.1 模板

c++提供两种模板机制:1函数模板;2类模板

3.1.1 函数模板

函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表

template<typename T>
//template声明创建模板//typename表面其后面的符号是一种数据类型,可以用class代替//T通用的数据类型,名称可以替换,通常为大写字母

使用模板注意事项:括号里的参数类型必须一致才行


//交换整型函数
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;
}//利用模板提供通用的交换函数
template<typename T>         //在函数的上一行写模板声明,告诉编译器这是个模板函数
void mySwap(T& a, T& b)      //注意使用模板a,b数据类型必须一致
{T temp = a;a = b;b = temp;
}void test01()
{int a = 10;int b = 20;//swapInt(a, b);                    普通实现//利用模板实现交换//1自动类型推导mySwap(a, b);                       //注意使用模板a,b数据类型必须一致//2显示指定类型,前面加尖括号和数据类型mySwap<int>(a, b);                  //注意使用模板a,b数据类型必须一致cout << "a = " << a << endl;cout << "b = " << b << endl;}int main() {test01();system("pause");return 0;
}

使用模板,数据类型必须一致,但是模板可以分别对不同类型数据重复使用

//交换的函数模板
template<typename T>
void mySwap(T &a, T&b)
{T temp = a;a = b;b = temp;
}template<class T>                       //class也可以替换成typename
//利用选择排序,进行对数组从大到小的排序
void mySort(T arr[], int len)
{for (int i = 0; i < len; i++){int max = i;                    //最大数的下标for (int j = i + 1; j < len; j++){if (arr[max] < arr[j]){max = j;}}if (max != i)                   //如果最大数的下标不是i,交换两者{mySwap(arr[max], arr[i]);}}
}
template<typename T>
void printArray(T arr[], int len) {for (int i = 0; i < len; i++) {cout << arr[i] << " ";}cout << endl;
}
void test01()
{//测试char数组char charArr[] = "bdcfeagh";int num = sizeof(charArr) / sizeof(char);mySort(charArr, num);printArray(charArr, num);
}void test02()
{//测试int数组int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };int num = sizeof(intArr) / sizeof(int);mySort(intArr, num);printArray(intArr, num);
}int main() {test01();test02();system("pause");return 0;
}

普通函数调用时可以发生自动类型转换

模板函数自动类型推导方式不会发生自动类型转换 ,显式指定类型方式会

//普通函数
int myAdd01(int a, int b)
{return a + b;
}
//函数模板
template<class T>
T myAdd02(T a, T b)       //返回值类型也用T,因为不确定要传进去什么,所以返回也不定
{return a + b;
}
//使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换
void test01()
{int a = 10;int b = 20;char c = 'c';cout << myAdd01(a, c) << endl; //正确,将char类型的'c'隐式转换为int类型ASCII码 99//myAdd02(a, c); // 报错,使用自动类型推导时,不会发生隐式类型转换myAdd02<int>(a, c); //正确,如果用显示指定类型,可以发生隐式类型转换
}int main() {test01();system("pause");return 0;
}

普通函数和模板函数调用优先:1如果函数模板和普通函数都可以实现优先调用普通函数;2可以通过空模板参数列表来强制调用函数模板;3函数模板也可以发生重载;4如果函数模板可以产生更好的匹配优先调用函数模板

//普通函数与函数模板调用规则
void myPrint(int a, int b)
{cout << "调用的普通函数" << endl;
}template<typename T>
void myPrint(T a, T b)
{ cout << "调用的模板" << endl;
}template<typename T>
void myPrint(T a, T b, T c)
{ cout << "调用重载的模板" << endl;
}void test01()
{//1如果函数模板和普通函数都可以实现,优先调用普通函数//如果告诉编译器有普通函数但只是声明没有实现或者不在当前文件内实现,会报错找不到int a = 10;int b = 20;myPrint(a, b);                //同时满足,优先调用普通函数//2可以通过空模板参数列表来强制调用函数模板myPrint<>(a, b); //调用函数模板//3函数模板也可以发生重载int c = 30;myPrint(a, b, c);            //调用重载的函数模板//4如果函数模板可以产生更好的匹配,优先调用函数模板char c1 = 'a';char c2 = 'b';myPrint(c1, c2);             //调用函数模板
}int main() {test01();system("pause");return 0;
}

模板不是万能的,如果T是自定义类型(class、数组) 就无法使用模板,需要重载模板为自定义数据类型提供具体化模板

#include<iostream>
using namespace std;
#include <string>class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};//普通函数模板
template<class T>
bool myCompare(T& a, T& b)
{if (a == b){return true;}else{return false;}
}//具体化,是以template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<> bool myCompare(Person &p1, Person &p2)
{if ( p1.m_Name  == p2.m_Name && p1.m_Age == p2.m_Age){return true;}else{return false;}
}void test01()
{int a = 10;int b = 20;//内置数据类型可以直接使用通用的函数模板bool ret = myCompare(a, b);if (ret){cout << "a == b " << endl;}else{cout << "a != b " << endl;}
}void test02()
{Person p1("Tom", 10);Person p2("Tom", 10);//自定义数据类型,不会调用普通的函数模板//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型bool ret = myCompare(p1, p2);if (ret){cout << "p1 == p2 " << endl;}else{cout << "p1 != p2 " << endl;}
}int main() {test01();test02();system("pause");return 0;
}

3.1.2 类模板

#include <string>
//类模板
template<class NameType, class AgeType>       //类一般不止一个数据类型,此处两个T
class Person
{
public:Person(NameType name, AgeType age){this->mName = name;this->mAge = age;}void showPerson(){cout << "name: " << this->mName << " age: " << this->mAge << endl;}
public:NameType mName;                          //对应两个TAgeType mAge;                            //对应两个T
};void test01()
{// 指定NameType 为string类型,AgeType 为 int类型Person<string, int>P1("孙悟空", 999);P1.showPerson();
}int main() {test01();system("pause");return 0;
}

类模板特点:1类模板没有自动类型推导的使用方式,类模板使用只能用显示指定类型方式;2类模板中的模板参数列表可以有默认参数;

#include <string>
//类模板
template<class NameType, class AgeType = int>
class Person
{
public:Person(NameType name, AgeType age){this->mName = name;this->mAge = age;}void showPerson(){cout << "name: " << this->mName << " age: " << this->mAge << endl;}
public:NameType mName;AgeType mAge;
};//1类模板没有自动类型推导的使用方式
void test01()
{// Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导Person <string ,int>p("孙悟空", 1000); //必须使用显示指定类型的方式,使用类模板p.showPerson();
}//2类模板在模板参数列表中可以有默认参数
void test02()
{Person <string> p("猪八戒", 999); //类模板中的模板参数列表 可以指定默认参数p.showPerson();
}int main() {test01();test02();system("pause");return 0;
}

类模板与普通类:普通类中的成员函数一开始就可以创建,类模板中的成员函数在调用时才创建

class Person1
{
public:void showPerson1(){cout << "Person1 show" << endl;}
};class Person2
{
public:void showPerson2(){cout << "Person2 show" << endl;}
};template<class T>
class MyClass
{
public:T obj;                           //T可以是上面的Person1或者Person2//写的时候不出错说明类模板中的成员函数不是一开始就创建而是在模板调用时创建void fun1() { obj.showPerson1(); }void fun2() { obj.showPerson2(); }};void test01()
{MyClass<Person1> m;m.fun1();//m.fun2();                      //编译会出错,说明函数调用才会去创建成员函数
}int main() {test01();system("pause");return 0;
}

类模板对象做函数参数

一共三种方式:1指定传入的类型,直接显示对象的数据类型;2参数模板化,将对象中的参数变为模板进行传递;3整个类模板化,将这个对象类型模板化进行传递;

#include <string>
//类模板
template<class NameType, class AgeType = int>
class Person
{
public:Person(NameType name, AgeType age){this->mName = name;this->mAge = age;}void showPerson(){cout << "name: " << this->mName << " age: " << this->mAge << endl;}
public:NameType mName;AgeType mAge;
};//1指定传入的类型
void printPerson1(Person<string, int> &p)
{p.showPerson();
}
void test01()
{Person <string, int >p("孙悟空", 100);printPerson1(p);
}//2参数模板化
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("猪八戒", 90);printPerson2(p);
}//3整个类模板化
template<class T>
void printPerson3(T & p)
{cout << "T的类型为: " << typeid(T).name() << endl;p.showPerson();}
void test03()
{Person <string, int >p("唐僧", 30);printPerson3(p);
}int main() {test01();test02();test03();system("pause");return 0;
}

类模板与继承

类模板碰在继承时:1当子类继承的父类是一个类模板时,子类在声明的时候要指定出父类中T的类型,否则编译器无法给子类分配内存,如果想灵活指定出父类中T的类型,子类也需变为类模板

template<class T>
class Base
{T m;
};//class Son:public Base        //错误,必须知道父类中T的类型才可以向下继承
class Son :public Base<int>    //必须指定一个类型
{
};
void test01()
{Son c;
}//类模板继承类模板 ,可以用T指定父类中的T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:Son2(){cout << typeid(T1).name() << endl;   //typeid().name() 模板类自带的cout << typeid(T2).name() << endl;}
};void test02()
{Son2<int, char> child1;        //传入类型,一个T1一个T2
}int main() {test01();test02();system("pause");return 0;
}

类模板成员函数类外实现

#include <string>
//类模板中成员函数类外实现
template<class T1, class T2>
class Person {
public://成员函数类内声明Person(T1 name, T2 age);void showPerson();public: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;
}void test01()
{Person<string, int> p("Tom", 20);    p.showPerson();
}int main() {test01();system("pause");return 0;
}

类模板分文件编写

类模板中成员函数创建时机是在调用阶段导致分文件编写时链接不到

解决方式1:直接包含.cpp源文件;方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp

常用方式2示例如下

头文件person.hpp中写:

#pragma once                      //避免重复包含头文件
#include <iostream>
using namespace std;
#include <string>template<class T1, class T2>
class Person {
public:Person(T1 name, T2 age);void showPerson();
public: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;
}

源文件person.cpp中写:

#include<iostream>
using namespace std;//#include "person.h"
#include "person.cpp" //解决方式1,包含cpp源文件//解决方式2,将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"
void test01()
{Person<string, int> p("Tom", 10);p.showPerson();
}int main() {test01();system("pause");return 0;
}

类模板与友元:

全局函数类内实现,直接在类内声明友元即可

全局函数类外实现,需要提前让编译器知道全局函数的存在和类模板的存在

#include <string>//2全局函数配合友元  类外实现 - 先做函数模板声明
template<class T1, class T2> class Person;      //类外实现需要告诉模板类存在//如果声明了函数模板,可以将实现写到main后面
//template<class T1, class T2> void printPerson2(Person<T1, T2> & p); template<class T1, class T2>                   //类外实现需要告诉模板类存在
void printPerson2(Person<T1, T2> & p)          //类外实现需要告诉模板类存在
{                                              //类外实现需要告诉模板类存在cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}                                              //类外实现需要告诉模板类存在template<class T1, class T2>
class Person
{//1全局函数配合友元   类内实现friend void printPerson(Person<T1, T2> & p){cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;}//全局函数配合友元  类外实现friend void printPerson2<>(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;};//1全局函数在类内实现
void test01()
{Person <string, int >p("Tom", 20);printPerson(p);
}//2全局函数在类外实现
void test02()
{Person <string, int >p("Jerry", 30);printPerson2(p);
}int main() {//test01();test02();system("pause");return 0;
}

3.2 STL标准模板库

3.2.1 STL 基本概念

STL(Standard Template Library,标准模板库) 广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator) ,容器和算法之间通过迭代器进行连接; STL 几乎所有的代码都采用了模板类或者模板函数;

STL六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

1容器:各种数据结构,如vector、list、deque、set、map等用来存放数据

2算法:各种常用的算法,如sort、find、copy、for_each等

3迭代器:扮演了容器与算法之间的桥梁

4仿函数:行为类似函数,可作为算法的某种策略

5适配器:用来修饰容器或者仿函数或迭代器接口

6空间配置器:负责空间的配置与管理

3.2.2 容器算法迭代器

vector容器(与数组类似)代码示例

#include <vector>
#include <algorithm>          //包含标准算法头文件void MyPrint(int val)
{cout << val << endl;
}void test01() {vector<int> v;            //创建vector容器对象,并且通过模板参数指定容器中存放的数据类型//向容器中放数据v.push_back(10);v.push_back(20);v.push_back(30);v.push_back(40);//每一个容器都有自己的迭代器,迭代器是用来遍历容器中的元素//v.begin(),返回迭代器,这个迭代器指向容器中第一个数据//v.end()返回迭代器,这个迭代器指向容器元素最后一个元素的下一个位置//vector<int>::iterator 拿到vector<int>这种容器的迭代器类型vector<int>::iterator pBegin = v.begin(); //pBegin与pEnd是指针类型,输出需要解引用vector<int>::iterator pEnd = v.end();//第一种遍历方式:while (pBegin != pEnd) {cout << *pBegin << endl;pBegin++;}//第二种遍历方式:for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {cout << *it << endl;}//第三种遍历方式://使用STL提供标准遍历算法,头文件algorithmfor_each(v.begin(), v.end(), MyPrint);
}int main() {test01();system("pause");return 0;
}

vector存放自定义数据类型

#include <vector>
#include <string>//自定义数据类型
class Person {
public:Person(string name, int age) {mName = name;mAge = age;}
public:string mName;int mAge;
};
//存放对象
void test01() {vector<Person> v;//创建数据Person p1("aaa", 10);Person p2("bbb", 20);Person p3("ccc", 30);Person p4("ddd", 40);Person p5("eee", 50);v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);v.push_back(p5);for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {cout << "Name:" << (*it).mName << " Age:" << (*it).mAge << endl;}
}//放对象指针
void test02() {vector<Person*> v;//创建数据Person p1("aaa", 10);Person p2("bbb", 20);Person p3("ccc", 30);Person p4("ddd", 40);Person p5("eee", 50);v.push_back(&p1);v.push_back(&p2);v.push_back(&p3);v.push_back(&p4);v.push_back(&p5);for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {Person * p = (*it);cout << "Name:" << p->mName << " Age:" << (*it)->mAge << endl;}
}int main() {test01();test02();system("pause");return 0;
}

3.3 STL常用容器

3.3.1 string容器

string类内部封装了很多成员方法例如:查找find,拷贝copy,删除delete 替换replace,插入insert

#include <string>
//string构造
void test01()
{string s1; //创建空字符串,调用无参构造函数cout << "str1 = " << s1 << endl;          // str1=const char* str = "hello world";string s2(str);                           //hello worldcout << "str2 = " << s2 << endl;string s3(s2);                            //调用拷贝构造函数cout << "str3 = " << s3 << endl;string s4(10, 'a');cout << "str4 = " << s4 << endl;          //aaaaaaaaaa
}int main() {test01();system("pause");return 0;
}

给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赋给当前字符串

//赋值
void test01()
{string str1;str1 = "hello world";cout << "str1 = " << str1 << endl;string str2;str2 = str1;cout << "str2 = " << str2 << endl;string str3;str3 = 'a';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(5, 'x');cout << "str7 = " << str7 << endl;
}int main() {test01();system("pause");return 0;
}

string字符串拼接

//字符串拼接
void test01()
{string str1 = "我";str1 += "爱玩游戏";cout << "str1 = " << str1 << endl;string str2 = "LOL DNF";str1 += str2;cout << "str1 = " << str1 << endl;string str3 = "I";str3.append("love");str3.append("game abcde", 6);                //game a,截取6个字符//str3.append(str2);str3.append(str2, 4, 3);//从下标4位置开始 ,截取3个字符,从下标0开始数,空格也算一位cout << "str3 = " << str3 << endl;
}
int main() {test01();system("pause");return 0;
}

string查找和替换

find查找是从左往后,rfind从右往左
find找到字符串后返回查找的第一个字符位置,找不到返回-1
replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串

//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个字符为字符串str
//string& replace(int pos, int n,const char* s);  //替换从pos开始的n个字符为字符串s//查找和替换
void test01()
{//查找string str1 = "abcdefgde";int pos = str1.find("de");if (pos == -1){cout << "未找到" << endl;}else{cout << "pos = " << pos << endl;}pos = str1.rfind("de");cout << "pos = " << pos << endl;
}void test02()
{//替换string str1 = "abcdefgde";str1.replace(1, 3, "1111");cout << "str1 = " << str1 << endl;
}int main() {//test01();test02();system("pause");return 0;
}

string字符串比较(比较ASCLL码)

= 返回 0

> 返回 1

< 返回 -1

//int compare(const string &s) const;       //与字符串s比较
//int compare(const char *s) const;        //与字符串s比较//字符串比较
void test01()
{string s1 = "hello";string s2 = "aello";int ret = s1.compare(s2);if (ret == 0) {cout << "s1 等于 s2" << endl;}else if (ret > 0){cout << "s1 大于 s2" << endl;}else{cout << "s1 小于 s2" << endl;}}int main() {test01();system("pause");return 0;
}

string字符存取

void test01()
{string str = "hello world";for (int i = 0; i < str.size(); i++){cout << str[i] << " ";}cout << endl;for (int i = 0; i < str.size(); i++){cout << str.at(i) << " ";}cout << endl;//字符修改str[0] = 'x';str.at(1) = 'x';cout << str << endl;}int main() {test01();system("pause");return 0;
}

string插入和删除

//字符串插入和删除
void test01()
{string str = "hello";str.insert(1, "666");cout << str << endl;     //h666ellostr.erase(1, 3);         //从1号位置开始3个字符cout << str << endl;     //hello
}int main() {test01();system("pause");return 0;
}

string字串,从string字符串截取一小段

//子串
void test01()
{string str = "abcdefg";string subStr = str.substr(1, 3);cout << "subStr = " << subStr << endl;string email = "hello@sina.com";int pos = email.find("@");                   //5string username = email.substr(0, pos);cout << "username: " << username << endl;}int main() {test01();system("pause");return 0;
}

3.3.2 vector容器

vector数据结构和数组非常相似,也称为单端容器不同之处在于数组是静态空间,而vector可以动态扩展

#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());   //v2从v1的头部开始到v1的尾部结束printVector(v2);vector<int> v3(10, 100);                //10个100printVector(v3);vector<int> v4(v3);printVector(v4);
}int main() {test01();system("pause");return 0;
}

vector赋值

#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;v2 = v1;printVector(v2);vector<int>v3;v3.assign(v1.begin(), v1.end());printVector(v3);vector<int>v4;v4.assign(10, 100);printVector(v4);
}int main() {test01();system("pause");return 0;
}

vector容量和大小(capacity&size),容量大于大小

//empty();                     判断容器是否为空//capacity();                  容器的容量//size();                      返回容器中元素的个数//resize(int num);             重新指定容器的长度为num,若容器变长,则以默认值填充新位置​                              //如果容器变短,则末尾超出容器长度的元素被删除//resize(int num, elem);      重新指定容器的长度为num,若容器变长,则以elem值填充新位置
​                              //如果容器变短,则末尾超出容器长度的元素被删除#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);if (v1.empty()){cout << "v1为空" << endl;}else{cout << "v1不为空" << endl;cout << "v1的容量 = " << v1.capacity() << endl;cout << "v1的大小 = " << v1.size() << endl;}//resize 重新指定大小 ,若指定的更大,默认用0填充新位置,可以利用重载版本替换默认填充v1.resize(15,10);            //15个数,用10补齐printVector(v1);//resize 重新指定大小 ,若指定的更小,超出部分元素被删除v1.resize(5);                //0 1 2 3 4五个数printVector(v1);
}int main() {test01();system("pause");return 0;
}

vector插入和删除

push_back(ele);                                                //尾部插入元素ele
pop_back();                                                      //删除最后一个元素
insert(const_iterator pos, ele); //迭代器指向位置pos插入元素ele
insert(const_iterator pos, int count,ele);           //迭代器指向位置pos插入count个元素ele
erase(const_iterator pos);                                //删除迭代器指向的元素
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
clear();                                                              //删除容器中所有元素

#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;  v1.push_back(10);                   //尾插push_backv1.push_back(20);v1.push_back(30);v1.push_back(40);v1.push_back(50);printVector(v1);//尾删v1.pop_back();                      //尾删pop_backprintVector(v1);                    //10 20 30 40 //插入v1.insert(v1.begin(), 100);printVector(v1);                    //100 10 20 30 40 50v1.insert(v1.begin(), 2, 1000);printVector(v1);                    //1000 1000 100 10 20 30 40//删除v1.erase(v1.begin());               //1000 100 10 20 30 40printVector(v1);//清空v1.erase(v1.begin(), v1.end());v1.clear();printVector(v1);
}int main() {test01();system("pause");return 0;
}

vector数据存取(除了用迭代器获取vector容器中元素,[ ]和at也可以)

#include <vector>void test01()
{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的第一个元素为: " << v1.front() << endl;cout << "v1的最后一个元素为: " << v1.back() << endl;
}int main() {test01();system("pause");return 0;
}

vector互换容器,实现两个容器元素互换

#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;for (int i = 10; i > 0; i--){v2.push_back(i);}printVector(v2);//互换容器cout << "互换后" << endl;v1.swap(v2);printVector(v1);printVector(v2);
}void test02()
{vector<int> v;for (int i = 0; i < 100000; i++) {v.push_back(i);}cout << "v的容量为:" << v.capacity() << endl;    //138255cout << "v的大小为:" << v.size() << endl;        //100000v.resize(3);cout << "v的容量为:" << v.capacity() << endl;    //138255cout << "v的大小为:" << v.size() << endl;        //3//收缩内存vector<int>(v).swap(v);                          //匿名对象cout << "v的容量为:" << v.capacity() << endl;    //3cout << "v的大小为:" << v.size() << endl;        //3
}int main() {test01();test02();system("pause");return 0;
}

vector预留空间,减少vector在动态扩展容量时的扩展次数

#include <vector>void test01()
{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]) {             //num统计动态扩展几次p = &v[0];num++;}}cout << "num:" << num << endl;    //num=1,若预留空间为10000则为7
}int main() {test01();system("pause");return 0;
}

3.3.3 deque容器

双端数组,可以对头端进行插入删除操作
deque与vector区别:
vector对于头部的插入删除效率低,数据量越大,效率越低
deque相对而言,对头部的插入删除速度回比vector快
vector访问元素时的速度会比deque快,这和两者内部实现有关

deque构造

#include <deque>void printDeque(const deque<int>& d)
{for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {cout << *it << " ";}cout << endl;
}
//deque构造
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);                //10个100printDeque(d3);deque<int>d4 = d3;printDeque(d4);
}int main() {test01();system("pause");return 0;
}

deque赋值

#include <deque>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;d2 = d1;printDeque(d2);deque<int>d3;d3.assign(d1.begin(), d1.end());printDeque(d3);deque<int>d4;d4.assign(10, 100);printDeque(d4);}int main() {test01();system("pause");return 0;
}

deque大小操作

#include <deque>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);//判断容器是否为空if (d1.empty()) {cout << "d1为空!" << endl;}else {cout << "d1不为空!" << endl;//统计大小cout << "d1的大小为:" << d1.size() << endl;    //10}//重新指定大小d1.resize(15, 1);     //0 1 2 3 4 5 6 7 8 9 1 1 1 1 1,15位不够的用1补齐printDeque(d1); d1.resize(5);                                     // 0 1 2 3 4printDeque(d1);
}int main() {test01();system("pause");return 0;
}

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位置的数据,返回下一个数据的位置

#include <deque>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> d;//尾插d.push_back(10);d.push_back(20);//头插d.push_front(100);d.push_front(200);printDeque(d);//尾删d.pop_back();//头删d.pop_front();printDeque(d);
}//插入
void test02()
{deque<int> d;d.push_back(10);d.push_back(20);d.push_front(100);d.push_front(200);printDeque(d);d.insert(d.begin(), 1000);printDeque(d);d.insert(d.begin(), 2,10000);printDeque(d);deque<int>d2;d2.push_back(1);d2.push_back(2);d2.push_back(3);d.insert(d.begin(), d2.begin(), d2.end());printDeque(d);}//删除
void test03()
{deque<int> d;d.push_back(10);d.push_back(20);d.push_front(100);d.push_front(200);printDeque(d);d.erase(d.begin());printDeque(d);d.erase(d.begin(), d.end());d.clear();printDeque(d);
}int main() {//test01();//test02();test03();system("pause");return 0;
}

deque 数据存取

#include <deque>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> d;d.push_back(10);d.push_back(20);d.push_front(100);d.push_front(200);for (int i = 0; i < d.size(); i++) {cout << d[i] << " ";}cout << endl;for (int i = 0; i < d.size(); i++) {cout << d.at(i) << " ";}cout << endl;cout << "front:" << d.front() << endl;cout << "back:" << d.back() << endl;}int main() {test01();system("pause");return 0;
}

deque排序

#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> d;d.push_back(10);d.push_back(20);d.push_front(100);d.push_front(200);printDeque(d);sort(d.begin(), d.end());printDeque(d);}int main() {test01();system("pause");return 0;
}

deque示例(5名选手选手ABCDE,10个评委,去除最高分,去除最低分,取每个选手平均分)

//选手类
class Person
{
public:Person(string name, int score){this->m_Name = name;this->m_Score = score;}string m_Name; //姓名int m_Score;  //平均分
};void createPerson(vector<Person>&v)   //创建五个选手插入vector
{string nameSeed = "ABCDE";for (int i = 0; i < 5; i++){string name = "选手";name += nameSeed[i];int score = 0;Person p(name, score);//将创建的person对象放入到容器中v.push_back(p);}
}//打分
void setScore(vector<Person>&v)
{for (vector<Person>::iterator it = v.begin(); it != v.end(); it++){//将评委的分数 放入到deque容器中deque<int>d;for (int i = 0; i < 10; i++){int score = rand() % 41 + 60;  // 60 ~ 100d.push_back(score);}//cout << "选手: " << it->m_Name << " 打分: " << endl;//for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)//{//    cout << *dit << " ";//}//cout << endl;//排序sort(d.begin(), d.end());//去除最高和最低分d.pop_back();d.pop_front();//取平均分int sum = 0;for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++){sum += *dit; //累加每个评委的分数}int avg = sum / d.size();//将平均分 赋值给选手身上it->m_Score = avg;}
}void showScore(vector<Person>&v)
{for (vector<Person>::iterator it = v.begin(); it != v.end(); it++){cout << "姓名: " << it->m_Name << " 平均分: " << it->m_Score << endl;}
}int main() {//随机数种子srand((unsigned int)time(NULL));//1 创建5名选手vector<Person>v;  //存放选手容器createPerson(v);//测试//for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)//{//   cout << "姓名: " << (*it).m_Name << " 分数: " << (*it).m_Score << endl;//}//2 给5名选手打分setScore(v);//3 显示最后得分showScore(v);system("pause");return 0;
}

3.3.4 stack堆栈

stack一种先进后出的容器,只有一个出口;栈中进入数据称为入栈(push),栈中弹出数据称为出栈(pop)

特点:栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为

#include <stack>                     //包含stack头文件
//栈容器常用接口
void test01()
{//构造函数stack<int> s;//向栈中添加元素叫做 压栈/入栈s.push(10);s.push(20);s.push(30);while (!s.empty()) {//输出栈顶元素cout << "栈顶元素为: " << s.top() << endl;//弹出栈顶元素s.pop();}cout << "栈的大小为:" << s.size() << endl;
}int main() {test01();system("pause");return 0;
}

3.3.5 queue队列

queue是一种先进先出的队列容器,它有首尾两个出口;队列容器允许从一端新增元素,从另一端移除元素;队列中进数据称为入队(push),队列中出数据称为出队(pop)
特点:队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为

#include <queue>                    //包含queue头文件
#include <string>
class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};void test01() {//创建队列queue<Person> q;//准备数据Person p1("唐僧", 30);Person p2("孙悟空", 1000);Person p3("猪八戒", 900);Person p4("沙僧", 800);//向队列中添加元素  入队操作q.push(p1);q.push(p2);q.push(p3);q.push(p4);//队列不提供迭代器,更不支持随机访问 while (!q.empty()) {//输出队头元素cout << "队头元素-- 姓名: " << q.front().m_Name<< " 年龄: " << q.front().m_Age << endl;cout << "队尾元素-- 姓名: " << q.back().m_Name<< " 年龄: " << q.back().m_Age << endl;cout << endl;//弹出队头元素q.pop();}cout << "队列大小为:" << q.size() << endl;
}int main() {test01();system("pause");return 0;
}

3.3.6 list链表

链表(list):是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成,结点包含一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
特点:
1由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器;
2STL中的链表是一个双向循环链表;
3插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的;
优点:
1采用动态存储分配,不会造成内存浪费和溢出;
2链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素;
缺点:
1链表灵活,但是空间(指针域)和时间(遍历)耗费较大;

#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);           //构造函数,10个1000printList(L4);
}
int main()
{test01();system("pause");return 0;
}

list赋值和交换

#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;L2 = L1;printList(L2);list<int>L3;L3.assign(L2.begin(), L2.end());  //assign赋值printList(L3);list<int>L4;L4.assign(10, 100);               //10个100printList(L4);
}
//交换
void test02()
{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, 100);cout << "交换前: " << endl;printList(L1);printList(L2);cout << endl;L1.swap(L2);cout << "交换后: " << endl;printList(L1);printList(L2);
}int main(){//test01();test02();system("pause");return 0;
}

list容量和大小

#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);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);
}int main()
{test01();system("pause");return 0;
}

list数据存取

#include <list>//数据存取
void test01()
{list<int>L1;L1.push_back(10);L1.push_back(20);L1.push_back(30);L1.push_back(40);//cout << L1.at(0) << endl;           //错误 不支持at访问数据//cout << L1[0] << endl;              //错误  不支持[]方式访问数据cout << "第一个元素为: " << L1.front() << endl;cout << "最后一个元素为: " << L1.back() << endl;//list容器的迭代器是双向迭代器,不支持随机访问list<int>::iterator it = L1.begin();//it = it + 1;                       //错误,不可以跳跃访问,即使是+1
}int main()
{test01();system("pause");return 0;
}

list插入和删除

push_back(ele) :尾部插入元素ele
push_front(elem):开头插入元素ele
pop_front():从容器开头移除第一个元素
pop_back():删除容器中最后一个元素
insert(pos, ele):在pos位置插elem元素的拷贝,返回新数据的位置
insert(pos, int count,ele):在pos位置插入n个elem数据,无返回值
insert(pos,beg,end):在pos位置插入[beg,end)区间的数据,无返回值
erase(pos):删除pos位置的数据,返回下一个数据的位置
erase(start,end):删除[beg,end)区间的数据,返回下一个数据的位置
remove(elem):删除容器中所有与elem值匹配的元素
clear():删除容器中所有元素

#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> L;//尾插L.push_back(10);L.push_back(20);L.push_back(30);//头插L.push_front(100);L.push_front(200);L.push_front(300);printList(L);//尾删L.pop_back();printList(L);//头删L.pop_front();printList(L);//插入list<int>::iterator it = L.begin();L.insert(++it, 1000);printList(L);//删除it = L.begin();L.erase(++it);printList(L);//移除L.push_back(10000);L.push_back(10000);L.push_back(10000);printList(L);L.remove(10000);printList(L);//清空L.clear();printList(L);
}
int main()
{test01();system("pause");return 0;
}

list反转和排序

void printList(const list<int>& L) {for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {cout << *it << " ";}cout << endl;
}bool myCompare(int val1 , int val2)
{return val1 > val2;
}//反转和排序
void test01()
{list<int> L;L.push_back(90);L.push_back(30);L.push_back(20);L.push_back(70);printList(L);//反转容器的元素L.reverse();printList(L);//排序L.sort();            //默认的排序规则 从小到大printList(L);L.sort(myCompare);   //指定规则,从大到小printList(L);
}int main() {test01();system("pause");return 0;
}

3.3.7 set/ multiset容器

所有元素都会在插入时自动被排序,set/multiset属于关联式容器,底层结构是用二叉树实现;
set和multiset区别:set不允许容器中有重复的元素,multiset允许有重复的元素

#include <set>                       //包含头文件
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(30);s1.insert(20);s1.insert(40);printSet(s1);set<int>s2(s1);                //拷贝构造printSet(s2);set<int>s3;               s3 = s2;                       //赋值printSet(s3);
}int main() {test01();system("pause");return 0;
}

大小和交换

#include <set>
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(30);s1.insert(20);s1.insert(40);if (s1.empty()){cout << "s1为空" << endl;}else{cout << "s1不为空" << endl;cout << "s1的大小为: " << s1.size() << endl;}
}//交换
void test02()
{set<int> s1;s1.insert(10);s1.insert(30);s1.insert(20);s1.insert(40);set<int> s2;s2.insert(100);s2.insert(300);s2.insert(200);s2.insert(400);cout << "交换前" << endl;printSet(s1);printSet(s2);cout << endl;cout << "交换后" << endl;s1.swap(s2);printSet(s1);printSet(s2);
}int main() {//test01();test02();system("pause");return 0;
}

插入和删除

#include <set>
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(30);s1.insert(20);s1.insert(40);printSet(s1);//删除s1.erase(s1.begin());printSet(s1);s1.erase(30);printSet(s1);//清空s1.clear();             //s1.erase(s1.begin(), s1.end());printSet(s1);
}int main() {test01();system("pause");return 0;
}

查找和统计

#include <set>//查找和统计
void test01()
{set<int> s1;//插入s1.insert(10);s1.insert(30);s1.insert(20);s1.insert(40);//查找set<int>::iterator pos = s1.find(30);if (pos != s1.end()){cout << "找到了元素 : " << *pos << endl;}else{cout << "未找到元素" << endl;}//统计int num = s1.count(30);cout << "num = " << num << endl;
}int main() {test01();system("pause");return 0;
}

pair队组(利用队组返回两个成对出现的数据)

//语法
//pair<type, type> p ( value1, value2 )
//pair<type, type> p = make_pair( value1, value2 )#include <string>//对组创建
void test01()
{pair<string, int> p(string("Tom"), 20);cout << "姓名: " <<  p.first << " 年龄: " << p.second << endl;pair<string, int> p2 = make_pair("Jerry", 10);cout << "姓名: " << p2.first << " 年龄: " << p2.second << endl;
}int main() {test01();system("pause");return 0;
}

排序(默认从小到大,可以利用仿函数改变默认规则)

//默认数据类型示例#include <set>
class MyCompare                             //包含仿函数的类
{
public:bool operator()(int v1, int v2) {       //仿函数return v1 > v2;}
};
void test01()
{    set<int> s1;s1.insert(10);s1.insert(40);s1.insert(20);s1.insert(30);s1.insert(50);//默认从小到大for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) {cout << *it << " ";}cout << endl;//指定排序规则set<int,MyCompare> s2;s2.insert(10);s2.insert(40);s2.insert(20);s2.insert(30);s2.insert(50);for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {cout << *it << " ";}cout << endl;
}int main() {test01();system("pause");return 0;
}
//自定义数据类型示例#include <set>
#include <string>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){//按照年龄进行排序  降序return p1.m_Age > p2.m_Age;}
};void test01()
{set<Person, comparePerson> s;Person p1("刘备", 23);Person p2("关羽", 27);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() {test01();system("pause");return 0;
}

3.3.8 map/multimap

map中所有元素都是pair,pair中第一个元素为key(键值)起索引作用,第二个元素为value(实值),所有元素都会根据元素的键值自动排序

map和multimap区别:map不允许容器中有重复key值元素,multimap允许容器中有重复key值元素
优点:可以根据key值快速找到value值

#include <map>
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));printMap(m);map<int, int>m2(m); //拷贝构造printMap(m2);map<int, int>m3;m3 = m2;            //赋值printMap(m3);
}int main() {test01();system("pause");return 0;
}

大小和交换

#include <map>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));if (m.empty()){cout << "m为空" << endl;}else{cout << "m不为空" << endl;cout << "m的大小为: " << m.size() << endl;}
}//交换
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));map<int, int>m2;m2.insert(pair<int, int>(4, 100));m2.insert(pair<int, int>(5, 200));m2.insert(pair<int, int>(6, 300));cout << "交换前" << endl;printMap(m);printMap(m2);cout << "交换后" << endl;m.swap(m2);printMap(m);printMap(m2);
}int main() {test01();test02();system("pause");return 0;
}

插入和删除

#include <map>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(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.erase(m.begin(),m.end());m.clear();printMap(m);
}int main() {test01();system("pause");return 0;
}

查找统计

#include <map>//查找和统计
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));//查找map<int, int>::iterator pos = m.find(3);if (pos != m.end()){cout << "找到元素 key = " << (*pos).first << " value = " << (*pos).second ;}else{cout << "未找到元素" << endl;}//统计int num = m.count(3);cout << "num = " << num << endl;
}int main() {test01();system("pause");return 0;
}

排序

#include <map>class MyCompare {
public:bool operator()(int v1, int v2) {return v1 > v2;}
};void test01()
{//默认从小到大排序//利用仿函数实现从大到小排序map<int, int, MyCompare> m;m.insert(make_pair(1, 10));m.insert(make_pair(2, 20));m.insert(make_pair(3, 30));m.insert(make_pair(4, 40));m.insert(make_pair(5, 50));for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++) {cout << "key:" << it->first << " value:" << it->second << endl;}
}
int main() {test01();system("pause");return 0;
}

容器案例

公司招聘了10名新员工(ABCDEFGHIJ);部门分为:策划、美术、研发;
员工信息有: 姓名 工资组成;
随机给10名员工分配部门和工资
通过multimap进行信息的插入 key(部门编号) value(员工)

#include <string>
#include <vector>
#include <map>
#include <ctime>
#include <iostream>
//部门的编号
#define CEHUA 0   //策划
#define MEISHU 1  //美术
#define YANFA 2   //研发
using namespace std;
//员工类
class Worker
{
public:Worker(string name,int salary){this->name = name;this->salary = salary;}string name;//姓名int salary;//工资
};
//创建员工
void CreatWorker(vector<Worker>& w,int n)
{string name;int salary;for (int i = 0; i < n; i++){name= "员工";char temp = (char)('A' + i);name = name + temp;           //员工 ABCDEFG...salary = rand() % 1000 + 1000;//salary:1000-1999Worker worker(name, salary);//用name和salary实例化类w.push_back(worker);//将实例化的类放入vector容器中}}
void ClassifyWorker(multimap<int, Worker>& m, vector <Worker> &w,int n)
{for (int i = 0;i < n; i++){int id = rand() % 3;//0 1 2m.insert(make_pair(id, w.at(i)));}
}
void ShowWorkerByGourp(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.name << " 工资: " << pos->second.salary << endl;}cout << "----------------------" << endl;cout << "美术部门: " << endl;pos = m.find(MEISHU);count = m.count(MEISHU); // 统计具体人数index = 0;for (; pos != m.end() && index < count; pos++, index++){cout << "姓名: " << pos->second.name << " 工资: " << pos->second.salary << endl;}cout << "----------------------" << endl;cout << "研发部门: " << endl;pos = m.find(YANFA);count = m.count(YANFA); // 统计具体人数index = 0;for (; pos != m.end() && index < count; pos++, index++){cout << "姓名: " << pos->second.name << " 工资: " << pos->second.salary << endl;}
}
int main()
{srand((unsigned int)time(NULL));//随机数种子,为了产生随机工资和部门编号vector <Worker> w;              //员工信息multimap<int, Worker> m;        //部门编号 + 员工信息int number;                     //员工数目cout << "please input the number of employees:";cin >> number;//创建员工CreatWorker(w,number);//员工分部门ClassifyWorker(m,w, number);//按部门输出ShowWorkerByGourp(m);return 0;
}

3.4 STL函数对象

函数对象(仿函数)是一个类,不是一个函数

3.4.1 函数对象使用

特点:
函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
函数对象超出普通函数的概念,函数对象可以有自己的状态
函数对象可以作为参数传递

class MyAdd
{
public:MyAdd(){count = 0;}int count;//普通函数共性:可以有参数,返回值int operator()(int a, int b){//仿函数特性:可以有自己的状态count++;return a + b;}
};
//函数对象可以作为参数传递
void test(MyAdd& ma, int a, int b)
{cout<<ma(a, b)<<endl;
}
int main()
{MyAdd ma;cout << ma(10, 10) << endl;cout << ma(10, 20) << endl;cout << ma.count << endl;test(ma, 10, 30);return 0;
}

3.4.2 谓词

返回bool类型的仿函数称为谓词
如果operator()接受一个参数,那么叫做一元谓词
如果operator()接受两个参数,那么叫做二元谓词

#include <vector>
#include <algorithm>//一元谓词
struct GreaterFive{bool operator()(int val) {return val > 5;}
};void test01() {vector<int> v;for (int i = 0; i < 10; i++){v.push_back(i);}vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());if (it == v.end()) {cout << "没找到!" << endl;}else {cout << "找到:" << *it << endl;}}int main() {test01();system("pause");return 0;
}
#include <vector>
#include <algorithm>
//二元谓词
class MyCompare
{
public:bool operator()(int num1, int num2){return num1 > num2;}
};void test01()
{vector<int> v;v.push_back(10);v.push_back(40);v.push_back(20);v.push_back(30);v.push_back(50);//默认从小到大sort(v.begin(), v.end());for (vector<int>::iterator it = v.begin(); it != v.end(); it++){cout << *it << " ";}cout << endl;cout << "----------------------------" << endl;//使用函数对象改变算法策略,排序从大到小sort(v.begin(), v.end(), MyCompare());for (vector<int>::iterator it = v.begin(); it != v.end(); it++){cout << *it << " ";}cout << endl;
}int main() {test01();system("pause");return 0;
}

3.4.3 内建函数对象

分类:1算术仿函数;2关系仿函数;3逻辑仿函数;
用法:和一般函数完全相同,需要引入头文件 #include<functional>

算术仿函数

//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>      //取反仿函数void test1()
{//取反negate<int> n;cout << n(10) << endl;
}
void test2()
{//相加plus<int>p;cout << p(1, 2) << endl;
}
int main()
{test1();test2();return 0;
}

关系仿函数

//template<class T> bool equal_to<T>                                 //等于
//template<class T> bool not_equal_to<T> emplate<class T> T minus<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>                               //小于等于#include <functional>
#include <vector>
#include <algorithm>class MyCompare
{
public:bool operator()(int v1,int v2){return v1 > v2;}
};
void test01()
{vector<int> v;v.push_back(10);v.push_back(30);v.push_back(50);v.push_back(40);v.push_back(20);for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {cout << *it << " ";}cout << endl;//自己实现仿函数//sort(v.begin(), v.end(), MyCompare());//STL内建仿函数  大于仿函数sort(v.begin(), v.end(), greater<int>());for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {cout << *it << " ";}cout << endl;
}int main() {test01();system("pause");return 0;
}

逻辑仿函数

//template<class T> bool logical_and<T>   //逻辑与
//template<class T> bool logical_or<T>    //逻辑或
//template<class T> bool logical_not<T>   //逻辑非int main()
{vector<bool> v1;v1.push_back(true);v1.push_back(false);v1.push_back(true);v1.push_back(false);for (vector<bool>::iterator it = v1.begin(); it != v1.end(); it++){cout << *it;}cout << endl;//将v的元素放在v1中而且元素取反vector<bool> v2;v2.resize(v1.size());transform(v1.begin(), v1.end(), v2.begin(), logical_not<bool>());for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++){cout << *it;}cout << endl;
}

3.5 STL常用算法

stl常用算法主要是由头文件<algorithm> <functional> <numeric>组成
<algorithm>比较多,涉及到比较、 交换、查找、遍历操作、复制、修改等
<numeric>只包括几个在序列上面进行简单数学运算的模板函数
<functional>定义了一些模板类用以声明函数对象

3.5.1 常用遍历算法

for_each              //遍历容器

class  MyPrint
{
public:void operator()(int val){cout << val << " ";}
};
void Print1(int val)
{cout << val << " ";
}
int main()
{vector<int> v;for (int i = 0; i < 10; i++){v.push_back(i);}for_each(v.begin(), v.end(), Print1);cout << endl;for_each(v.begin(), v.end(), MyPrint());return 0;
}

transform            //搬运容器到另一个容器中

class TransForm
{
public:int operator()(int val){return val+10;             //返回原数据+10结果}
};
void print(int val)
{cout << val << " ";
}
int main()
{vector<int> v;for (int i = 0; i < 10; i++)v.push_back(i);vector<int> TargetV;          //目标容器TargetV.resize(v.size());     // 目标容器需要提前开辟空间transform(v.begin(), v.end(), TargetV.begin(), TransForm());for_each(TargetV.begin(), TargetV.end(), print);
}

3.5.2 常用查找算法

find                    //查找元素
find_if                //按条件查找元素
adjacent_find    //查找相邻重复元素
binary_search  //二分查找法
count                //统计元素个数
count_if            //按条件统计元素个数

find

#include <algorithm>
#include <vector>
#include <string>
void test01() {vector<int> v;for (int i = 0; i < 10; i++) {v.push_back(i + 1);}//查找容器中是否有 5 这个元素vector<int>::iterator it = find(v.begin(), v.end(), 5);if (it == v.end()) {cout << "没有找到!" << endl;}else {cout << "找到:" << *it << endl;}
}class Person {
public:Person(string name, int age) {this->m_Name = name;this->m_Age = age;}bool operator==(const Person& p)            //重载等号==运算符{if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {return true;}return false;}public:string m_Name;int m_Age;
};void test02() {vector<Person> v;//创建数据Person p1("aaa", 10);Person p2("bbb", 20);Person p3("ccc", 30);Person p4("ddd", 40);v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);vector<Person>::iterator it = find(v.begin(), v.end(), p2);if (it == v.end()) {cout << "没有找到!" << endl;}else {cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;}
}

find_if

#include <algorithm>
#include <vector>
#include <string>//内置数据类型
class GreaterFive
{
public:bool operator()(int val){return val > 5;}
};void test01() {vector<int> v;for (int i = 0; i < 10; i++) {v.push_back(i + 1);}vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());if (it == v.end()) {cout << "没有找到!" << endl;}else {cout << "找到大于5的数字:" << *it << endl;}
}//自定义数据类型
class Person {
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}
public:string m_Name;int m_Age;
};class Greater20
{
public:bool operator()(Person &p){return p.m_Age > 20;}};void test02() {vector<Person> v;//创建数据Person p1("aaa", 10);Person p2("bbb", 20);Person p3("ccc", 30);Person p4("ddd", 40);v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);vector<Person>::iterator it = find_if(v.begin(), v.end(), Greater20());if (it == v.end()){cout << "没有找到!" << endl;}else{cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;}
}int main() {//test01();test02();system("pause");return 0;
}

adjacent_find

#include <algorithm>
#include <vector>void test01()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(5);v.push_back(2);v.push_back(4);v.push_back(4);v.push_back(3);//查找相邻重复元素vector<int>::iterator it = adjacent_find(v.begin(), v.end());if (it == v.end()) {cout << "找不到!" << endl;}else {cout << "找到相邻重复元素为:" << *it << endl;}
}

binary_search

二分查找法查找效率很高,返回true/false,需要注意的是查找的容器中元素必须的是有序序列

#include <algorithm>
#include <vector>void test01()
{vector<int>v;for (int i = 0; i < 10; i++){v.push_back(i);}//二分查找bool ret = binary_search(v.begin(), v.end(),2);if (ret){cout << "找到了" << endl;}else{cout << "未找到" << endl;}
}int main() {test01();system("pause");return 0;
}

count

#include <algorithm>
#include <vector>//内置数据类型
void test01()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(4);v.push_back(5);v.push_back(3);v.push_back(4);v.push_back(4);int num = count(v.begin(), v.end(), 4);cout << "4的个数为: " << num << endl;
}//自定义数据类型
class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}bool operator==(const Person & p){if (this->m_Age == p.m_Age){return true;}else{return false;}}string m_Name;int m_Age;
};void test02()
{vector<Person> v;Person p1("刘备", 35);Person p2("关羽", 35);Person p3("张飞", 35);Person p4("赵云", 30);Person p5("曹操", 25);v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);v.push_back(p5);Person p("诸葛亮",35);int num = count(v.begin(), v.end(), p);cout << "num = " << num << endl;
}
int main() {//test01();test02();system("pause");return 0;
}

count_if

#include <algorithm>
#include <vector>class Greater4
{
public:bool operator()(int val){return val >= 4;}
};//内置数据类型
void test01()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(4);v.push_back(5);v.push_back(3);v.push_back(4);v.push_back(4);int num = count_if(v.begin(), v.end(), Greater4());cout << "大于4的个数为: " << num << endl;
}//自定义数据类型
class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};class AgeLess35
{
public:bool operator()(const Person &p){return p.m_Age < 35;}
};
void test02()
{vector<Person> v;Person p1("刘备", 35);Person p2("关羽", 35);Person p3("张飞", 35);Person p4("赵云", 30);Person p5("曹操", 25);v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);v.push_back(p5);int num = count_if(v.begin(), v.end(), AgeLess35());cout << "小于35岁的个数:" << num << endl;
}int main() {//test01();test02();system("pause");return 0;
}

3.5.3 常用排序算法

sort                       //对容器内元素进行排序
random_shuffle    //洗牌 指定范围内的元素随机调整次序
merge                  // 容器元素合并,并存储到另一容器中
reverse                // 反转指定范围的元素

sort

#include <algorithm>
#include <vector>void myPrint(int val)
{cout << val << " ";
}void test01() {vector<int> v;v.push_back(10);v.push_back(30);v.push_back(50);v.push_back(20);v.push_back(40);//sort默认从小到大排序sort(v.begin(), v.end());for_each(v.begin(), v.end(), myPrint);cout << endl;//从大到小排序sort(v.begin(), v.end(), greater<int>());for_each(v.begin(), v.end(), myPrint);cout << endl;
}int main() {test01();system("pause");return 0;
}

random_shuffle(需要加入随机数种子,不加执行一次可以多次执行是重复的结果)

#include <algorithm>
#include <vector>
#include <ctime>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{srand((unsigned int)time(NULL));           //随机种子vector<int> v;for(int i = 0 ; i < 10;i++){v.push_back(i);}for_each(v.begin(), v.end(), myPrint());cout << endl;//打乱顺序random_shuffle(v.begin(), v.end());for_each(v.begin(), v.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

merge

#include <algorithm>
#include <vector>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v1;vector<int> v2;for (int i = 0; i < 10 ; i++) {v1.push_back(i);v2.push_back(i + 1);}vector<int> vtarget;//目标容器需要提前开辟空间vtarget.resize(v1.size() + v2.size());//合并  需要两个有序序列merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vtarget.begin());for_each(vtarget.begin(), vtarget.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

reverse

#include <algorithm>
#include <vector>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v;v.push_back(10);v.push_back(30);v.push_back(50);v.push_back(20);v.push_back(40);cout << "反转前: " << endl;for_each(v.begin(), v.end(), myPrint());cout << endl;cout << "反转后: " << endl;reverse(v.begin(), v.end());for_each(v.begin(), v.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

3.5.4 常用拷贝和替换算法

copy            // 容器内指定范围的元素拷贝到另一容器中
replace        // 将容器内指定范围的元素修改为新元素
replace_if    // 容器内指定范围满足条件的元素替换为新元素
swap           // 互换两个容器的元素

copy

#include <algorithm>
#include <vector>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v1;for (int i = 0; i < 10; i++) {v1.push_back(i + 1);}vector<int> v2;v2.resize(v1.size());copy(v1.begin(), v1.end(), v2.begin());for_each(v2.begin(), v2.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

replace

#include <algorithm>
#include <vector>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v;v.push_back(20);v.push_back(30);v.push_back(20);v.push_back(40);v.push_back(50);v.push_back(10);v.push_back(20);cout << "替换前:" << endl;for_each(v.begin(), v.end(), myPrint());cout << endl;cout << "替换后:" << endl;replace(v.begin(), v.end(), 10,11);      //将容器中的10 替换成 11for_each(v.begin(), v.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

replace_if

#include <algorithm>
#include <vector>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};class ReplaceGreater30
{
public:bool operator()(int val){return val >= 30;}};void test01()
{vector<int> v;v.push_back(20);v.push_back(30);v.push_back(20);v.push_back(40);v.push_back(50);v.push_back(10);v.push_back(20);cout << "替换前:" << endl;for_each(v.begin(), v.end(), myPrint());cout << endl;//将容器中大于等于的30 替换成 3000cout << "替换后:" << endl;replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);for_each(v.begin(), v.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

swap

#include <algorithm>
#include <vector>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v1;vector<int> v2;for (int i = 0; i < 10; i++) {v1.push_back(i);v2.push_back(i+100);}cout << "交换前: " << endl;for_each(v1.begin(), v1.end(), myPrint());cout << endl;for_each(v2.begin(), v2.end(), myPrint());cout << endl;cout << "交换后: " << endl;swap(v1, v2);for_each(v1.begin(), v1.end(), myPrint());cout << endl;for_each(v2.begin(), v2.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

3.5.5 常用算术生成算法

算术生成算法使用时包含的头文件为 #include <numeric>
accumulate                                       //计算容器内元素累加总和
fill                                                      // 向容器中添加元素

accumulate

#include <numeric>
#include <vector>
void test01()
{vector<int> v;for (int i = 0; i <= 100; i++) {v.push_back(i);}int total = accumulate(v.begin(), v.end(), 0);cout << "total = " << total << endl;     //5050
}int main() {test01();system("pause");return 0;
}

fill

#include <numeric>
#include <vector>
#include <algorithm>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v;v.resize(10);fill(v.begin(), v.end(), 100);            //填充for_each(v.begin(), v.end(), myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

3.5.6 常用集合算法

set_intersection              // 求两个容器的交集

set_union                       // 求两个容器的并集

set_difference                // 求两个容器的差集

set_intersection 求交集

#include <vector>
#include <algorithm>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v1;vector<int> v2;for (int i = 0; i < 10; i++){v1.push_back(i);v2.push_back(i+5);}vector<int> vTarget;//取两个里面较小的值给目标容器开辟空间vTarget.resize(min(v1.size(), v2.size()));//返回目标容器的最后一个元素的迭代器地址vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());for_each(vTarget.begin(), itEnd, myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

set_union 求并集

#include <vector>
#include <algorithm>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v1;vector<int> v2;for (int i = 0; i < 10; i++) {v1.push_back(i);v2.push_back(i+5);}vector<int> vTarget;//取两个容器的和给目标容器开辟空间vTarget.resize(v1.size() + v2.size());//返回目标容器的最后一个元素的迭代器地址vector<int>::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());for_each(vTarget.begin(), itEnd, myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

set_difference 求差集

求差集的两个集合必须是有序序列
目标容器开辟的空间取两个容器的大值

#include <vector>
#include <algorithm>class myPrint
{
public:void operator()(int val){cout << val << " ";}
};void test01()
{vector<int> v1;vector<int> v2;for (int i = 0; i < 10; i++) {v1.push_back(i);v2.push_back(i+5);}vector<int> vTarget;//取两个里面较大的值给目标容器开辟空间vTarget.resize( max(v1.size() , v2.size()));//返回目标容器的最后一个元素的迭代器地址cout << "v1与v2的差集为: " << endl;     //0 1 2 3 4vector<int>::iterator itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());for_each(vTarget.begin(), itEnd, myPrint());cout << endl;cout << "v2与v1的差集为: " << endl;     //10 11 12 13 14itEnd = set_difference(v2.begin(),v2.end(),v1.begin(),v1.end(),vTarget.begin());for_each(vTarget.begin(), itEnd, myPrint());cout << endl;
}int main() {test01();system("pause");return 0;
}

哔哩哔哩黑马程序员C++课程个人学习笔记相关推荐

  1. 【168天】黑马程序员27天视频学习笔记【Day07】

    [168天]黑马程序员27天视频学习笔记[Day07] 叨逼叨两句 决定了,我会记住这一天! 07-01:构造方法Constructor概述和格式 构造方法概述和作用 给对象的数据(属性)进行初始化. ...

  2. 【C++】黑马程序员-C++核心编程学习笔记

    前言 根据黑马程序员C++课程内容,结合讲义,将自己学习C++的过程中将自己觉得有必要记下的笔记进行整理,方便复习回顾,编程环境为VSCode. 本阶段主要针对C++面向对象编程技术做详细讲解,探讨C ...

  3. 黑马程序员8日python学习笔记

    黑马程序员的教程给小白的体验非常良好,讲的非常细致,甚至会介绍一下鼠标快捷键的设置,会提到一些新手很容易踩到的坑. 打开python win+R python 看到>>>(进入到解释 ...

  4. 黑马程序员--分布式搜索ElasticSearch学习笔记

    写在最前 黑马视频地址:https://www.bilibili.com/video/BV1LQ4y127n4/ 想获得最佳的阅读体验,请移步至我的个人博客 SpringCloud学习笔记 消息队列M ...

  5. 【176天】黑马程序员27天视频学习笔记【Day11-上】

    叨逼叨两句 正式结束了,之后就是收尾工作. 今天想休息一下,任务就不做满了. 未来定个标准,事不过三,一天的任务,最多分三天完成,超过要罚分. 11-(01-04):Eclipse使用方法 程序的编译 ...

  6. 【208天】黑马程序员27天视频学习笔记【Day21-中】

    叨逼叨两句 今天又了结一件事,好累,好爽. 这几天在哥们家玩,更新内容不会断,但会相对少些了. 21-09:将文本反转 流对象要尽量晚开早关 package com.test.demo001;impo ...

  7. 【186天】黑马程序员27天视频学习笔记【Day15-上】

    叨逼叨两句 今天和朋友聊了聊,感觉真的很好,人还是得多跟别人交流,才能发现自己一些观念和理论上的漏洞. 今天帮了大家一个忙,总算要接近尾声了,开心啊. 我要准备去做一件伟大的事! 很高兴姐姐迈开了这一 ...

  8. 【C++】黑马程序员C++核心编程学习笔记(完结)

    目录 前言: 一.内存 1.1 内存四区 1.2 new操作符 二.引用 2.1 引用基本使用和注意事项 2.2 引用做函数参数 2.3 引用函数返回值 2.3 常量引用 三.函数提高 3.1 函数默 ...

  9. 黑马程序员之ASP.NET学习笔记:Http状态代码及其含义

    -----------------------------------2345王牌技术员联盟.2345王牌技术员联盟.期待与您交流!---------------------------------- ...

最新文章

  1. 针对《评人工智能如何走向新阶段》一文,继续发布国内外的跟贴留言427-438条如下:
  2. 现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?...
  3. Python计算机视觉:第九章 图像分割
  4. NSTimer注意内存泄露(真该死)
  5. 数据填充规则之PKCS7
  6. google 确定某点海拔高_一份“高投资回报率”的用户体验度量方法指南
  7. MySQL 事务控制语句(TCL)
  8. php 获取src,html-使用PHP获取img src
  9. rk3399_android7.1耳机拔插ADC检测
  10. 软件开发合同模板范本
  11. 『网易实习』周记(五)
  12. 2分钟搞定收货地址三级联动,数据易于维护,更新。
  13. 入行 AI,如何选个脚踏实地的岗位?
  14. 哈工大计算机系统2022春 大作业 程序人生
  15. Gem5 O3 可视化
  16. 【NOIP2015】洛谷2668 斗地主
  17. Pulse local frame(画出光脉冲三维动态演化过程)
  18. 那些会休息的人是如何度过假期的?
  19. Windows Touchpad 报告描述符实例
  20. Redis设计与实现-笔记(一)

热门文章

  1. 程序员去哪个城市工作更好?我选择深圳的几个理由
  2. 安卓 时间服务器_王者荣耀更新!安卓iOS这个限制,终于消失了
  3. 项目经理如何带好一支技术团队?
  4. 【U8+】用友U8明细账联查凭证的时候提示“联查凭证不存在”
  5. GGE 双标图如何看?
  6. 手游实时对战初步解决方案
  7. 每天只要8分钱 瑞星企业终端安全管理系统软件SOHO版全面保护小微企业安全
  8. 10分钟教你用python下载和拼接微信好友头像图片
  9. 英雄联盟怎么查服务器维护时间,《英雄联盟》维护时间查询官网入口在哪 维护时间查询官网...
  10. 证券交易的量化接口的用途?