写在前面:此博文仅作为个人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 常用集合算法
#include
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" <
常量有两种: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++变量必须赋初始值
关键字
变量和常量名可以包含字母、数字、下划线区分大小写,不可以数字作为开头,同时避开关键字
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 //使用字符串需要包含字符串的头文件,字符不用
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对应a
cout << ch << endl;
int main() {
bool flag = true;
cout << flag << endl; // 1
flag = false;
cout << flag << endl; // 0
cout << "size of bool = " << sizeof(bool) << endl; //bool类型占1字节
system("pause");
return 0;
}
输入cin和输出cout
int a = 0;
cout << "请输入整型变量:" << endl;
cin >> a;
cout << a << endl;
只有整形才能取模,除法分母不能为零
//加减乘除
int main() {
int a1 = 10;
int b1 = 3;
cout << a1 + b1 << endl;
cout << a1 - b1 << endl;
cout << a1 * b1 << endl;
cout << a1 / b1 << endl; //两个整数相除结果依然是整数,3
double d1 = 0.5;
double d2 = 0.25;
int d3=0;
cout << d1 / d2 << endl; //两个小数可以相除
//cout << d1 / d3 << endl; 报错,除数不可以为0
cout << 10 % 3 << endl; //取模,结果为1
double 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; // 1
cout << (a > b) << endl; // 0
cout << (a <= b) << endl; // 1
system("pause");
return 0;
}
逻辑运算符
int main() {
int a = 10;
int b = 10;
cout << !a << endl; // 0 逻辑非 真变假,假变真
cout << !!a << endl; // 1
int c = 10;
int d = 0;
cout << (c && d) << endl;// 0 逻辑与 同真为真,其它为假
cout << (c || d) << endl;// 1 逻辑或 有一个真,就为真
system("pause");
return 0;
}
顺序结构:程序按顺序执行
选择结构:依据条件是否满足,有选择的执行代码
循环结构:依据条件是否满足,循环多次执行某段代码
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否则执行b
cout << "c = " << c << endl; //20
(a > b ? a : b) = 100; //C++中三目运算符返回的是变量,可以继续赋值
cout << "b = " << b << endl; //b=100
system("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、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 9
num++;
} 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 5
cout << "2" << endl;
cout << "3" << endl;
cout << "4" << endl;
FLAG:
cout << "5" << endl;
system("pause");
return 0;
}
所谓数组,就是一个集合,里面存放了相同类型的数据元素,数组由连续的内存位置组成
//一维数组的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;
}
返回值类型 函数名 (参数列表)
{
函数体语句
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 20
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl; // 20 10
cout << "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还是10
cout << "mian中的 b = " << b << endl; //a=10 b=20
system("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
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
#include"swap.h"
int main() {
int a = 100;
int b = 200;
swap(a, b);
system("pause");
return 0;
}
指针变量定义语法: 数据类型 * 变量名;
指针变量和普通变量的区别:普通变量存放的是数据,指针变量存放的是地址
指针变量可以通过 * 操作符操作指针变量指向的内存空间,这个过程称为解引用
int main() {
int a = 10; //定义整型变量a
//指针定义语法: 数据类型 * 变量名 ;
int * p;
p = &a; //指针变量赋值指针指向变量a的地址,&是取址符
//以上两行等价于 int * p=&a;
cout << &a << endl; //打印数据a的地址 010FF968
cout << p << endl; //打印指针变量p 010FF968
//指针的使用 通过*操作指针变量指向的内存
cout << "*p = " << *p << endl; //10
system("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还是10
swap2(&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;
}
//定义结构体时struct不可省略,创建时可以
//结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
}stu3; //结构体变量创建方式3,定义的时候就创建一个结构体变量。定义结构体需要加分号;
int main() {
//结构体变量创建方式1
struct student stu1; //struct 关键字可以省略
stu1.name = "张三";
stu1.age = 18;
stu1.score = 100;
cout << "姓名:" << stu1.name << " 年龄:" << stu1.age << " 分数:" << stu1.score << endl;
//结构体变量创建方式2
struct 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};
//值传递 //还是18
printStudent(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++程序在执行时,将内存划分为4个区域,不同区域存放的数据赋予不同的生命周期;
代码区:存放函数体的二进制代码,由操作系统进行管理
全局区:存放全局变量和静态变量以及常量
栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
堆区:由程序员分配和释放,若程序员不释放程序结束时由操作系统自动释放
代码区:存放 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;
}
栈区:由编译器自动分配释放;存放函数的参数值、局部变量等;
注意事项:不要返回局部变量的地址;栈区开辟的数据由编译器自动释放;
int * func()
{
int a = 10;
return &a;
}
int main() {
int *p = func();
cout << *p << endl; //10
cout << *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; //10
cout << *p << endl; //10
system("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;
}
语法: 数据类型 &别名 = 原名
作用:给变量起别名,引用必须初始化,且初始化后不可改变
int main() {
int a = 10;
int b = 20;
//int &c; 错误,引用必须初始化
int &c = a; //初始化
c = b; //这是赋值操作,不是更改引用,改引用是int &c =b;
cout << "a = " << a << endl; //20
cout << "b = " << b << endl; //20
cout << "c = " << c << endl; //20
system("pause");
return 0;
}
引用作函数参数时,形参可以修饰实参 ,引用可以看作简化指针
//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=20
mySwap02(&a, &b);
cout << "a:" << a << " b:" << b << endl; //a=20 b=10
mySwap03(a, b);
cout << "a:" << a << " b:" << b << endl; //a=20 b=10
system("pause");
return 0;
}
//返回局部变量引用
int& test01() {
int a = 10; //局部变量
return a;
}
//返回静态变量引用
int& test02() {
static int a = 20;
return a;
}
int main() {
//不能返回局部变量的引用
int& ref = test01();
cout << "ref = " << ref << endl; //10
cout << "ref = " << ref << endl; //不是10
//如果函数做左值,那么必须返回引用
int& ref2 = test02();
cout << "ref2 = " << ref2 << endl; //20
cout << "ref2 = " << ref2 << endl; //20
test02() = 1000; //函数做为左值可以重新赋值
cout << "ref2 = " << ref2 << endl; //1000
cout << "ref2 = " << ref2 << endl; //1000
system("pause");
return 0;
}
//引用本质是指针常量 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;
}
在函数形参列表中,可以加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;
}
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; //50
cout << "ret = " << func(100) << endl; //120
cout << "ret = " << func2(20, 20) << endl; //40
system("pause");
return 0;
}
C++中函数的形参列表里可以有占位参数,用来占位,调用函数时必须填补该位置参数
//函数占位参数 ,占位参数也可以有默认参数
void func(int a, int ,b=10) { //int类型占位,b是默认参数
cout << "this is func" << endl;
}
int main() {
func(10,10); //占位参数必须填补
system("pause");
return 0;
}
函数重载:函数名字可以相同,提高复用性
重载条件: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); //调用无const
func(10);//调用有const
//func2(10); //碰到默认参数产生歧义,需要避免
system("pause");
return 0;
}
c++面向对象的3大特性:封装、继承、多态
具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类
人可以作为对象,属性有姓名、年龄、身高、体重…,行为有走、跑、吃饭…
车可以作为对象,属性有颜色、品牌、尺寸、价格…,行为有跑、放音乐、放空调…
语法: class 类名{ 访问权限: 属性 / 行为 };
封装的意义:将属性和行为作为一个整体,用来表现生活中的事物
封装圆类示例:
//圆周率
const double PI = 3.14;
//封装一个圆类,求圆的周长
//class代表设计一个类,后面跟着的是类名
class Circle
{
public: //访问权限 公共的权限
//属性
int m_r;//半径
//行为
double calculateZC() //获取到圆的周长
{
//获取圆的周长 2 * pi * r
return 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;
}
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 180
cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl; //18 180
}
int main() {
test01();
system("pause");
return 0;
}
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;
}
在成员变量和成员函数前加上关键字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; //100
Person p2;
p2.m_A = 200;
cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据,现在是200
cout << "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;
}
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); //40
cout << "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为空返回了,没有输出mAge
int main() {
test01();
system("pause");
return 0;
}
常函数: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;
}
友元的目的就是让一个函数或者类访问另一个类中私有成员;友元的关键字为 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;
}
运算符重载:对已有的运算符重新进行定义,以适应自定义数据类型运算
加号运算符重载
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对象只能有一个所以用引用的方式out
out << "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; //20
cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
//200,匿名对象调用:类名() ,匿名对象调完就释放
}
int main() {
test01();
test02();
system("pause");
return 0;
}
继承的语法: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就是100
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl; //100
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl; //200
cout << "st.m_Age = " << st.m_Age << endl; //200
}
int main() {
test01();
system("pause");
return 0;
}
多态分为两类
静态多态: 函数重载和运算符重载复用函数名属于静态多态,静态多态函数地址早绑定 - 编译阶段确定函数地址
动态多态: 派生类(子类)和虚函数实现运行时多态,动态多态的函数地址晚绑定 - 运行阶段确定函数地址
多态条件: 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
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;
}
程序运行时产生的数据都属于临时数据,程序运行结束时会被释放,通过文件可以将数据保存下来
文件类型分为两种:1文本文件以ASCII码形式存储;2 二进制文件以二进制形式存储,不便阅读
操作文件的三大类: 1 ofstream写操作;2 ifstream读操作;3 fstream读写操作
文件打开方式利用|操作符可以配合使用,例如:用二进制方式写文件 ios::binary | ios:: out
写文件示例:
#include //包含头文件
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 //包含头文件
#include
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
#include
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
#include
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++泛型编程和STL(Standard Template Library,标准模板库)探讨C++更深层的使用
c++提供两种模板机制:1函数模板;2类模板
函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
template
//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 //在函数的上一行写模板声明,告诉编译器这是个模板函数
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(a, b); //注意使用模板a,b数据类型必须一致
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main() {
test01();
system("pause");
return 0;
}
使用模板,数据类型必须一致,但是模板可以分别对不同类型数据重复使用
//交换的函数模板
template
void mySwap(T &a, T&b)
{
T temp = a;
a = b;
b = temp;
}
template //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
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
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(a, c); //正确,如果用显示指定类型,可以发生隐式类型转换
}
int main() {
test01();
system("pause");
return 0;
}
普通函数和模板函数调用优先:1如果函数模板和普通函数都可以实现优先调用普通函数;2可以通过空模板参数列表来强制调用函数模板;3函数模板也可以发生重载;4如果函数模板可以产生更好的匹配优先调用函数模板
//普通函数与函数模板调用规则
void myPrint(int a, int b)
{
cout << "调用的普通函数" << endl;
}
template
void myPrint(T a, T b)
{
cout << "调用的模板" << endl;
}
template
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
using namespace std;
#include
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
//普通函数模板
template
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;
}
#include
//类模板
template //类一般不止一个数据类型,此处两个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; //对应两个T
AgeType mAge; //对应两个T
};
void test01()
{
// 指定NameType 为string类型,AgeType 为 int类型
PersonP1("孙悟空", 999);
P1.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}
类模板特点:1类模板没有自动类型推导的使用方式,类模板使用只能用显示指定类型方式;2类模板中的模板参数列表可以有默认参数;
#include
//类模板
template
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 p("孙悟空", 1000); //必须使用显示指定类型的方式,使用类模板
p.showPerson();
}
//2类模板在模板参数列表中可以有默认参数
void test02()
{
Person 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 MyClass
{
public:
T obj; //T可以是上面的Person1或者Person2
//写的时候不出错说明类模板中的成员函数不是一开始就创建而是在模板调用时创建
void fun1() { obj.showPerson1(); }
void fun2() { obj.showPerson2(); }
};
void test01()
{
MyClass m;
m.fun1();
//m.fun2(); //编译会出错,说明函数调用才会去创建成员函数
}
int main() {
test01();
system("pause");
return 0;
}
类模板对象做函数参数
一共三种方式:1指定传入的类型,直接显示对象的数据类型;2参数模板化,将对象中的参数变为模板进行传递;3整个类模板化,将这个对象类型模板化进行传递;
#include
//类模板
template
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 &p)
{
p.showPerson();
}
void test01()
{
Person p("孙悟空", 100);
printPerson1(p);
}
//2参数模板化
template
void printPerson2(Person&p)
{
p.showPerson();
cout << "T1的类型为: " << typeid(T1).name() << endl;
cout << "T2的类型为: " << typeid(T2).name() << endl;
}
void test02()
{
Person p("猪八戒", 90);
printPerson2(p);
}
//3整个类模板化
template
void printPerson3(T & p)
{
cout << "T的类型为: " << typeid(T).name() << endl;
p.showPerson();
}
void test03()
{
Person p("唐僧", 30);
printPerson3(p);
}
int main() {
test01();
test02();
test03();
system("pause");
return 0;
}
类模板与继承
类模板碰在继承时:1当子类继承的父类是一个类模板时,子类在声明的时候要指定出父类中T的类型,否则编译器无法给子类分配内存,如果想灵活指定出父类中T的类型,子类也需变为类模板
template
class Base
{
T m;
};
//class Son:public Base //错误,必须知道父类中T的类型才可以向下继承
class Son :public Base //必须指定一个类型
{
};
void test01()
{
Son c;
}
//类模板继承类模板 ,可以用T指定父类中的T类型
template
class Son2 :public Base
{
public:
Son2()
{
cout << typeid(T1).name() << endl; //typeid().name() 模板类自带的
cout << typeid(T2).name() << endl;
}
};
void test02()
{
Son2 child1; //传入类型,一个T1一个T2
}
int main() {
test01();
test02();
system("pause");
return 0;
}
类模板成员函数类外实现
#include
//类模板中成员函数类外实现
template
class Person {
public:
//成员函数类内声明
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数 类外实现
template
Person::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数 类外实现
template
void Person::showPerson() { //类模板中成员函数类外实现时,需要加上模板参数列表
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
void test01()
{
Person p("Tom", 20);
p.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}
类模板分文件编写
类模板中成员函数创建时机是在调用阶段导致分文件编写时链接不到
解决方式1:直接包含.cpp源文件;方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp
常用方式2示例如下
头文件person.hpp中写:
#pragma once //避免重复包含头文件
#include
using namespace std;
#include
template
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数 类外实现
template
Person::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数 类外实现
template
void Person::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
源文件person.cpp中写:
#include
using namespace std;
//#include "person.h"
#include "person.cpp" //解决方式1,包含cpp源文件
//解决方式2,将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"
void test01()
{
Person p("Tom", 10);
p.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}
类模板与友元:
全局函数类内实现,直接在类内声明友元即可
全局函数类外实现,需要提前让编译器知道全局函数的存在和类模板的存在
#include
//2全局函数配合友元 类外实现 - 先做函数模板声明
template class Person; //类外实现需要告诉模板类存在
//如果声明了函数模板,可以将实现写到main后面
//template void printPerson2(Person & p);
template //类外实现需要告诉模板类存在
void printPerson2(Person & p) //类外实现需要告诉模板类存在
{ //类外实现需要告诉模板类存在
cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
} //类外实现需要告诉模板类存在
template
class Person
{
//1全局函数配合友元 类内实现
friend void printPerson(Person & p)
{
cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}
//全局函数配合友元 类外实现
friend void printPerson2<>(Person & 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 p("Tom", 20);
printPerson(p);
}
//2全局函数在类外实现
void test02()
{
Person p("Jerry", 30);
printPerson2(p);
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
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空间配置器:负责空间的配置与管理
vector容器(与数组类似)代码示例
#include
#include //包含标准算法头文件
void MyPrint(int val)
{
cout << val << endl;
}
void test01() {
vector v; //创建vector容器对象,并且通过模板参数指定容器中存放的数据类型
//向容器中放数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
//每一个容器都有自己的迭代器,迭代器是用来遍历容器中的元素
//v.begin(),返回迭代器,这个迭代器指向容器中第一个数据
//v.end()返回迭代器,这个迭代器指向容器元素最后一个元素的下一个位置
//vector::iterator 拿到vector这种容器的迭代器类型
vector::iterator pBegin = v.begin(); //pBegin与pEnd是指针类型,输出需要解引用
vector::iterator pEnd = v.end();
//第一种遍历方式:
while (pBegin != pEnd) {
cout << *pBegin << endl;
pBegin++;
}
//第二种遍历方式:
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
//第三种遍历方式:
//使用STL提供标准遍历算法,头文件algorithm
for_each(v.begin(), v.end(), MyPrint);
}
int main() {
test01();
system("pause");
return 0;
}
vector存放自定义数据类型
#include
#include
//自定义数据类型
class Person {
public:
Person(string name, int age) {
mName = name;
mAge = age;
}
public:
string mName;
int mAge;
};
//存放对象
void test01() {
vector 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::iterator it = v.begin(); it != v.end(); it++) {
cout << "Name:" << (*it).mName << " Age:" << (*it).mAge << endl;
}
}
//放对象指针
void test02() {
vector 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::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;
}
string类内部封装了很多成员方法例如:查找find,拷贝copy,删除delete 替换replace,插入insert
#include
//string构造
void test01()
{
string s1; //创建空字符串,调用无参构造函数
cout << "str1 = " << s1 << endl; // str1=
const char* str = "hello world";
string s2(str); //hello world
cout << "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; //h666ello
str.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 = "[email protected]";
int pos = email.find("@"); //5
string username = email.substr(0, pos);
cout << "username: " << username << endl;
}
int main() {
test01();
system("pause");
return 0;
}
vector数据结构和数组非常相似,也称为单端容器不同之处在于数组是静态空间,而vector可以动态扩展
#include //包含头文件
void printVector(vector& v) {
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector v1; //无参构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
vector v2(v1.begin(), v1.end()); //v2从v1的头部开始到v1的尾部结束
printVector(v2);
vector v3(10, 100); //10个100
printVector(v3);
vector v4(v3);
printVector(v4);
}
int main() {
test01();
system("pause");
return 0;
}
vector赋值
#include
void printVector(vector& v) {
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//赋值操作
void test01()
{
vector v1; //无参构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
vectorv2;
v2 = v1;
printVector(v2);
vectorv3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
vectorv4;
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
void printVector(vector& v) {
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector 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
void printVector(vector& v) {
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//插入和删除
void test01()
{
vector v1;
v1.push_back(10); //尾插push_back
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
printVector(v1);
//尾删
v1.pop_back(); //尾删pop_back
printVector(v1); //10 20 30 40
//插入
v1.insert(v1.begin(), 100);
printVector(v1); //100 10 20 30 40 50
v1.insert(v1.begin(), 2, 1000);
printVector(v1); //1000 1000 100 10 20 30 40
//删除
v1.erase(v1.begin()); //1000 100 10 20 30 40
printVector(v1);
//清空
v1.erase(v1.begin(), v1.end());
v1.clear();
printVector(v1);
}
int main() {
test01();
system("pause");
return 0;
}
vector数据存取(除了用迭代器获取vector容器中元素,[ ]和at也可以)
#include
void test01()
{
vectorv1;
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
void printVector(vector& v) {
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vectorv1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
vectorv2;
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 v;
for (int i = 0; i < 100000; i++) {
v.push_back(i);
}
cout << "v的容量为:" << v.capacity() << endl; //138255
cout << "v的大小为:" << v.size() << endl; //100000
v.resize(3);
cout << "v的容量为:" << v.capacity() << endl; //138255
cout << "v的大小为:" << v.size() << endl; //3
//收缩内存
vector(v).swap(v); //匿名对象
cout << "v的容量为:" << v.capacity() << endl; //3
cout << "v的大小为:" << v.size() << endl; //3
}
int main() {
test01();
test02();
system("pause");
return 0;
}
vector预留空间,减少vector在动态扩展容量时的扩展次数
#include
void test01()
{
vector 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;
}
双端数组,可以对头端进行插入删除操作
deque与vector区别:
vector对于头部的插入删除效率低,数据量越大,效率越低
deque相对而言,对头部的插入删除速度回比vector快
vector访问元素时的速度会比deque快,这和两者内部实现有关
deque构造
#include
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//deque构造
void test01() {
deque d1; //无参构造函数
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
deque d2(d1.begin(),d1.end());
printDeque(d2);
dequed3(10,100); //10个100
printDeque(d3);
dequed4 = d3;
printDeque(d4);
}
int main() {
test01();
system("pause");
return 0;
}
deque赋值
#include
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//赋值操作
void test01()
{
deque d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
dequed2;
d2 = d1;
printDeque(d2);
dequed3;
d3.assign(d1.begin(), d1.end());
printDeque(d3);
dequed4;
d4.assign(10, 100);
printDeque(d4);
}
int main() {
test01();
system("pause");
return 0;
}
deque大小操作
#include
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//大小操作
void test01()
{
deque 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 4
printDeque(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
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//两端操作
void test01()
{
deque 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 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);
dequed2;
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 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
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//数据存取
void test01()
{
deque 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
#include
void printDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01()
{
deque 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&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&v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
//将评委的分数 放入到deque容器中
dequed;
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60; // 60 ~ 100
d.push_back(score);
}
//cout << "选手: " << it->m_Name << " 打分: " << endl;
//for (deque::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::iterator dit = d.begin(); dit != d.end(); dit++)
{
sum += *dit; //累加每个评委的分数
}
int avg = sum / d.size();
//将平均分 赋值给选手身上
it->m_Score = avg;
}
}
void showScore(vector&v)
{
for (vector::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名选手
vectorv; //存放选手容器
createPerson(v);
//测试
//for (vector::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;
}
stack一种先进后出的容器,只有一个出口;栈中进入数据称为入栈(push),栈中弹出数据称为出栈(pop)
特点:栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为
#include //包含stack头文件
//栈容器常用接口
void test01()
{
//构造函数
stack 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;
}
queue是一种先进先出的队列容器,它有首尾两个出口;队列容器允许从一端新增元素,从另一端移除元素;队列中进数据称为入队(push),队列中出数据称为出队(pop)
特点:队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
#include //包含queue头文件
#include
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 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;
}
链表(list):是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成,结点包含一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
特点:
1由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器;
2STL中的链表是一个双向循环链表;
3插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的;
优点:
1采用动态存储分配,不会造成内存浪费和溢出;
2链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素;
缺点:
1链表灵活,但是空间(指针域)和时间(遍历)耗费较大;
#include //包含头文件
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
listL1; //构造函数
L1.push_back(10); //尾插
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
listL2(L1.begin(),L1.end());//构造函数
printList(L2);
listL3(L2); //拷贝构造
printList(L3);
listL4(10, 1000); //构造函数,10个1000
printList(L4);
}
int main()
{
test01();
system("pause");
return 0;
}
list赋值和交换
#include
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//赋值和交换
void test01()
{
listL1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
//赋值
listL2;
L2 = L1;
printList(L2);
listL3;
L3.assign(L2.begin(), L2.end()); //assign赋值
printList(L3);
listL4;
L4.assign(10, 100); //10个100
printList(L4);
}
//交换
void test02()
{
listL1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
listL2;
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
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//大小操作
void test01()
{
listL1;
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
//数据存取
void test01()
{
listL1;
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::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
void printList(const list& L)
{
for (list::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//插入和删除
void test01()
{
list 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::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& L) {
for (list::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 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;
}
所有元素都会在插入时自动被排序,set/multiset属于关联式容器,底层结构是用二叉树实现;
set和multiset区别:set不允许容器中有重复的元素,multiset允许有重复的元素
#include //包含头文件
void printSet(set & s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//构造和赋值
void test01()
{
set s1; //构造函数
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
printSet(s1);
sets2(s1); //拷贝构造
printSet(s2);
sets3;
s3 = s2; //赋值
printSet(s3);
}
int main() {
test01();
system("pause");
return 0;
}
大小和交换
#include
void printSet(set & s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//大小
void test01()
{
set 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 s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
set 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
void printSet(set & s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//插入和删除
void test01()
{
set 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
//查找和统计
void test01()
{
set s1;
//插入
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
//查找
set::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 p ( value1, value2 )
//pair p = make_pair( value1, value2 )
#include
//对组创建
void test01()
{
pair p(string("Tom"), 20);
cout << "姓名: " << p.first << " 年龄: " << p.second << endl;
pair p2 = make_pair("Jerry", 10);
cout << "姓名: " << p2.first << " 年龄: " << p2.second << endl;
}
int main() {
test01();
system("pause");
return 0;
}
排序(默认从小到大,可以利用仿函数改变默认规则)
//默认数据类型示例
#include
class MyCompare //包含仿函数的类
{
public:
bool operator()(int v1, int v2) { //仿函数
return v1 > v2;
}
};
void test01()
{
set s1;
s1.insert(10);
s1.insert(40);
s1.insert(20);
s1.insert(30);
s1.insert(50);
//默认从小到大
for (set::iterator it = s1.begin(); it != s1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//指定排序规则
set s2;
s2.insert(10);
s2.insert(40);
s2.insert(20);
s2.insert(30);
s2.insert(50);
for (set::iterator it = s2.begin(); it != s2.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
test01();
system("pause");
return 0;
}
//自定义数据类型示例
#include
#include
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 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::iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
}
}
int main() {
test01();
system("pause");
return 0;
}
map中所有元素都是pair,pair中第一个元素为key(键值)起索引作用,第二个元素为value(实值),所有元素都会根据元素的键值自动排序
map和multimap区别:map不允许容器中有重复key值元素,multimap允许容器中有重复key值元素
优点:可以根据key值快速找到value值
#include
大小和交换
#include
插入和删除
#include
查找统计
#include
排序
#include
容器案例
公司招聘了10名新员工(ABCDEFGHIJ);部门分为:策划、美术、研发;
员工信息有: 姓名 工资组成;
随机给10名员工分配部门和工资
通过multimap进行信息的插入 key(部门编号) value(员工)
#include
#include
#include
函数对象(仿函数)是一个类,不是一个函数
特点:
函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
函数对象超出普通函数的概念,函数对象可以有自己的状态
函数对象可以作为参数传递
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<
返回bool类型的仿函数称为谓词
如果operator()接受一个参数,那么叫做一元谓词
如果operator()接受两个参数,那么叫做二元谓词
#include
#include
//一元谓词
struct GreaterFive{
bool operator()(int val) {
return val > 5;
}
};
void test01() {
vector v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector::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
#include
//二元谓词
class MyCompare
{
public:
bool operator()(int num1, int num2)
{
return num1 > num2;
}
};
void test01()
{
vector 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::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
cout << "----------------------------" << endl;
//使用函数对象改变算法策略,排序从大到小
sort(v.begin(), v.end(), MyCompare());
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main() {
test01();
system("pause");
return 0;
}
分类:1算术仿函数;2关系仿函数;3逻辑仿函数;
用法:和一般函数完全相同,需要引入头文件 #include
算术仿函数
//template T plus //加法仿函数
//template T minus //减法仿函数
//template T multiplies //乘法仿函数
//template T divides //除法仿函数
//template T modulus //取模仿函数
//template T negate //取反仿函数
void test1()
{
//取反
negate n;
cout << n(10) << endl;
}
void test2()
{
//相加
plusp;
cout << p(1, 2) << endl;
}
int main()
{
test1();
test2();
return 0;
}
关系仿函数
//template bool equal_to //等于
//template bool not_equal_to emplate T minus //不等于
//template bool greater //大于
//template bool greater_equal //大于等于
//template bool less //小于
//template bool less_equal //小于等于
#include
#include
#include
class MyCompare
{
public:
bool operator()(int v1,int v2)
{
return v1 > v2;
}
};
void test01()
{
vector v;
v.push_back(10);
v.push_back(30);
v.push_back(50);
v.push_back(40);
v.push_back(20);
for (vector::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());
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
test01();
system("pause");
return 0;
}
逻辑仿函数
//template bool logical_and //逻辑与
//template bool logical_or //逻辑或
//template bool logical_not //逻辑非
int main()
{
vector v1;
v1.push_back(true);
v1.push_back(false);
v1.push_back(true);
v1.push_back(false);
for (vector::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it;
}
cout << endl;
//将v的元素放在v1中而且元素取反
vector v2;
v2.resize(v1.size());
transform(v1.begin(), v1.end(), v2.begin(), logical_not());
for (vector::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it;
}
cout << endl;
}
stl常用算法主要是由头文件
for_each //遍历容器
class MyPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void Print1(int val)
{
cout << val << " ";
}
int main()
{
vector 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 v;
for (int i = 0; i < 10; i++)
v.push_back(i);
vector TargetV; //目标容器
TargetV.resize(v.size()); // 目标容器需要提前开辟空间
transform(v.begin(), v.end(), TargetV.begin(), TransForm());
for_each(TargetV.begin(), TargetV.end(), print);
}
find //查找元素
find_if //按条件查找元素
adjacent_find //查找相邻重复元素
binary_search //二分查找法
count //统计元素个数
count_if //按条件统计元素个数
find
#include
#include
#include
void test01() {
vector v;
for (int i = 0; i < 10; i++) {
v.push_back(i + 1);
}
//查找容器中是否有 5 这个元素
vector::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 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::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
#include
#include
//内置数据类型
class GreaterFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test01() {
vector v;
for (int i = 0; i < 10; i++) {
v.push_back(i + 1);
}
vector::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 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::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
#include
void test01()
{
vector 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::iterator it = adjacent_find(v.begin(), v.end());
if (it == v.end()) {
cout << "找不到!" << endl;
}
else {
cout << "找到相邻重复元素为:" << *it << endl;
}
}
binary_search
二分查找法查找效率很高,返回true/false,需要注意的是查找的容器中元素必须的是有序序列
#include
#include
void test01()
{
vectorv;
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
#include
//内置数据类型
void test01()
{
vector 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 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
#include
class Greater4
{
public:
bool operator()(int val)
{
return val >= 4;
}
};
//内置数据类型
void test01()
{
vector 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 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;
}
sort //对容器内元素进行排序
random_shuffle //洗牌 指定范围内的元素随机调整次序
merge // 容器元素合并,并存储到另一容器中
reverse // 反转指定范围的元素
sort
#include
#include
void myPrint(int val)
{
cout << val << " ";
}
void test01() {
vector 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());
for_each(v.begin(), v.end(), myPrint);
cout << endl;
}
int main() {
test01();
system("pause");
return 0;
}
random_shuffle(需要加入随机数种子,不加执行一次可以多次执行是重复的结果)
#include
#include
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
srand((unsigned int)time(NULL)); //随机种子
vector 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
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector v1;
vector v2;
for (int i = 0; i < 10 ; i++)
{
v1.push_back(i);
v2.push_back(i + 1);
}
vector 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
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector 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;
}
copy // 容器内指定范围的元素拷贝到另一容器中
replace // 将容器内指定范围的元素修改为新元素
replace_if // 容器内指定范围满足条件的元素替换为新元素
swap // 互换两个容器的元素
copy
#include
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i + 1);
}
vector 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
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector 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 替换成 11
for_each(v.begin(), v.end(), myPrint());
cout << endl;
}
int main() {
test01();
system("pause");
return 0;
}
replace_if
#include
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
class ReplaceGreater30
{
public:
bool operator()(int val)
{
return val >= 30;
}
};
void test01()
{
vector 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 替换成 3000
cout << "替换后:" << 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
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector v1;
vector 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;
}
算术生成算法使用时包含的头文件为 #include
accumulate //计算容器内元素累加总和
fill // 向容器中添加元素
accumulate
#include
#include
void test01()
{
vector 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
#include
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector 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;
}
set_intersection // 求两个容器的交集
set_union // 求两个容器的并集
set_difference // 求两个容器的差集
set_intersection 求交集
#include
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector v1;
vector v2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i+5);
}
vector vTarget;
//取两个里面较小的值给目标容器开辟空间
vTarget.resize(min(v1.size(), v2.size()));
//返回目标容器的最后一个元素的迭代器地址
vector::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
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector v1;
vector v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
v2.push_back(i+5);
}
vector vTarget;
//取两个容器的和给目标容器开辟空间
vTarget.resize(v1.size() + v2.size());
//返回目标容器的最后一个元素的迭代器地址
vector::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
#include
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector v1;
vector v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
v2.push_back(i+5);
}
vector vTarget;
//取两个里面较大的值给目标容器开辟空间
vTarget.resize( max(v1.size() , v2.size()));
//返回目标容器的最后一个元素的迭代器地址
cout << "v1与v2的差集为: " << endl; //0 1 2 3 4
vector::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 14
itEnd = 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;
}
千里之行始于足下