1.简单案例
#include<iostream>
using namespace std;
int main() {
cout << "hello world" << endl;
system("pause");
return 0;
}
2.变量存在的意义:方便管理内存空间
3.变量
#include<iostream>
using namespace std;
int main() {
int a = 10;
cout << "a="<<a<< endl;
system("pause");
return 0;
}
4.常量
#define 常量名 常量值
const 数据类型 常量名=常量值
#include<iostream>
using namespace std;
#define Day 7
int main() {
int a = 10;
cout <<"一周共有:" << Day<<"天" << endl;
system("pause");
return 0;
}
5.数据类型:给变量分配合适的内存空间
短整型(short):-32768~32767
整型(int)
长整型(long)
长长整型(longlong)
6.sizeof关键字可以统计数据类型所占内存大小
7.float单精度 (7位)
double双精度(15-16位)
两者的区别在于表示的有效数字范围不同
8.科学计数法
#include<iostream>
using namespace std;
#define Day 7
int main() {
float f1 = 3e2;//3*10^2
cout <<"f1:" <<f1<< endl;
float f2 = 3e-2;//3*0.1^2
cout << "f1: " << f2 << endl;
system("pause");
return 0;
}
9.字符型:字符型变量用于显示单个字符
char ch='a';
//单引号,不用双引号
//单引号内只能有一个字符,不可以是字符串
//a:97 A:65
10.转义字符
\a 警报
\b 退格,将当前位置移到前一列
\n 换行,将当前位置移到下一行开头
\\代表一个反斜线字符“\”
\t水平制表 更整齐的输出数据
\r 将当前位置移到本行开头
11.字符串型
C风格字符串
char str[]="hello world"//必须有中括号后面才能双引号;
C++风格字符串
string str2="hello world";
//包含一个头文件,#include<string>
12.布尔类型bool
bool flag = true;
cout << flag << endl;//输出为1
//本质上1代表真的值,0代表假的值
cout << "bool类型所占空间" << sizeof(bool) << endl;输出为1
13.数据的输入
int a = 0;
cout << "请给整型变量进行赋值" << endl;
cin >> a;
cout <<"整型变量a="<< a << endl;
bool flag = false;
cout << "请给布尔类型flag赋值" << endl;
cin >> flag;
cout << "布尔类型flag= " << flag << endl;//任意非0数值均为1
14.运算符
算术运算符
int a1 = 10;
int b1 = 3;
cout << a1 / b1 << endl;
//两个整数相除,结果依然是整数,将小数部分去除
//取模运算时,除数也不能为0
//两个小数不可以取模
//前置递增先对变量进行++,再计算表达式
//后置递增先计算表达式,后对变量进行++
15.赋值运算符
将表达式的值赋给变量
int a = 10;
a -= 2;
cout << "a= " << a << endl;
16.逻辑运算符
int a = 10;
int b = 10;
cout <<!a<< endl;
cout << (a && b) << endl;
cout << (a || b) << endl;
17.三目运算符
表达式1?表达式2:表达式
在C++三目运算符返回的是变量,可以继续赋值
18.switch语句
switch(表达式)
{
case 结果1:执行语句;break;
default:执行语句;break;
}
//switch判断时候只能是整型或者字符串,不可以是一个区间
//结构清晰,执行效率高
int num = rand() % 100 + 1;
cout << num << endl;//系统生成随机数
srand((unsigned int)time(NULL));
#include<ctime>
do while会先执行一次循环语句,再判断循环条件
//水仙花数
int num = 100;
do {
int a = 0;
int b = 0;
int c = 0;
a = num % 10;//个位
b = num / 10 % 10;//十位
c = num / 100;//百位
if (a * a * a + b * b * b + c * c * c==num) {
cout << num << endl;
}
num++;
} while (num < 1000);
19.for循环
外层执行一次,内层执行一周
//九九乘法表
int main() {
for (int i = 1; i < 10; i++)
{
for (int j = 1; j <= i; j++) {
cout << j << "*" << i << "=" << j * i << " ";
}
cout << endl;
}
system("pause");
return 0;
}
20.跳转语句
break使用时机
出现在switch语句
出现在循环语句中
出现在嵌套循环语句中
continue:跳过本次循环中余下尚未执行的语句,继续执行下一次循环
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0) {
continue;
}
}
//输出全为奇数
备注:continue不会使整个循环而终止,而break会跳出循环
21.goto语句
可以无条件跳转语句
//goto语句
cout << "1,xxxx" << endl;
goto FLAG;
cout << "2,xxxx" << endl;
FLAG:
cout << "3,xxxx" << endl;
22.数组
//所谓数组,就是一个集合,里面存放了相同类型的数据元素
数组是由连续的内存位置组成的
数组中的每个数据元素都是相同的数据类型
数据类型 数组名[数组长度];
数据类型 数组名[数组长度={值1,值2,...};
数据类型 数组名[]
数组名用途
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;
cout << "数组中第一个元素的地址: " << (int) & arr[1] << endl;
数组名是常量不可以赋值
可以通过数组名获取到数组首地址
可以获取整个数组占用内存空间大小
//二维数组定义方式
数据类型 数组名[行数][列数]
数据类型 数组名[行数][列数]={{数据1,数据2},{数据3,数据4}}
数据类型 数组名[行数][列数]={数据1,数据2,数据3,数据4}
数据类型 数组名[][列数]={数据1,数据2,数据3,数据4}
23.函数
值传递时,如果形参发生改变,并不会影响实参
函数的声明:提前告诉编译器的存在
声明可以多次,但是定义只能有一次
函数的分文件编写
创建.h后缀名的头文件
创建.cpp后缀名的源文件
在头文件中写函数的声明
在源文件中写函数的定义
#include "swap.h"
24.指针
指针就是一个地址
在32位操作系统下,指针是占4个字节空间大小,不管是什么数据类型
在64位操作系统下,指针是8个字节
空指针:指针变量指向内存中编号为0的空间
空指针用于给指针变量进行初始化;
空指针是不可以进行访问的
int *p=NULL;
指针变量指向非法的内存空间
int *p=(int *)0x1100;
25.const
const修饰指针--常量指针
//指针的指向可以修改,但是指针指向的值不可以改
const int *p=&a;
const修饰常量--指针常量
int * const p=&a;
//指针的指向不可以改,指针指向的值可以改
const既修饰指针,又修饰常量
//指针的指向和指针指向的值都不可以改
26.指针和数组
如果不想修改实参,就用值传递,如果想修改实参,就用地址传递
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "第一个元素为:" << arr[0] << endl;
int* p = arr;
cout << "利用指针访问第一个元素" << *p << endl;
p++;
cout << "利用指针访问第二个元素" << *p << endl;
int* p2 = arr;
for (int i = 0; i < 10; i++) {
cout << *p2 << endl;
p2++;
}
system("pause");
return 0;
}
27.结构体
结构体属于用户自定义的数据类型,允许用户存储不用的数据类型
struct Student {
string name;
int age;
int score;
};
struct Student s2 = {};
int main() {
struct Student s1;
s1.name = "张三";
s1.age = 18;
s1.score = 100;
cout << "姓名:" << s1.name << " 年龄:" << s1.age << "分数: " << s1.score << endl;
system("pause");
return 0;
}
28.结构体数组
#include<iostream>
using namespace std;
#include<string.h>
struct Student {
string name;
int age;
int score;
};
int main() {
struct Student stuArray[3] = {
{"张三",18,100},{"李四",28,99},{"王五",38,66}
};
stuArray[2].name = "赵六";
stuArray[2].age = 80;
stuArray[2].score = 60;
for (int i = 0; i < 3; i++) {
cout << "姓名:" << stuArray[i].name << " 年龄:" << stuArray[i].age << "分数: " << stuArray[i].score << endl;
}
system("pause");
return 0;
}
29.结构体指针
作用:通过指针访问结构体中的成员
#include<iostream>
using namespace std;
#include<string.h>
struct Student {
string name;
int age;
int score;
};
int main() {
Student s = { "张三",18,100 };
Student* p = &s;
cout << "姓名:" << p->name << " 年龄:" << p->age << "分数: " << p->score << endl;
system("pause");
return 0;
}
30.结构体做函数参数
作用:将结构体作为参数向函数中传递
#include<iostream>
using namespace std;
#include<string.h>
struct student {
string name;
int age;
int score;
};
void printStudent1(struct student s) {
cout << "子函数中 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;
}
void printStudent2(struct student *p) {
cout << "姓名" << p->name << "年龄" << p->age << "分数"<<p->score<<endl;
}
int main() {
struct student s;
s.name = "张三";
s.age = 20;
s.score = 85;
cout << "main函数中打印 姓名: " << s.name << "年龄:" << s.age << "分数:" << s.score << endl;
printStudent1(s);
printStudent2(&s);
system("pause");
return 0;
}
31.内存四区-----程序执行前
C++程序在执行时,将内存大方向划分为4区域
代码区:存放函数体的二进制代码,由操作系统进行管理
全局区:存放全局变量和静态变量以及常量
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
代码区:存放CPU执行的机器指令
代码区是共享的,是只读的
全局区:
全局变量和静态变量存放在此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统构成
总结:C++在程序运行前分为全局区和代码区
代码区的特点是共享和只读
全局区中存放全局变量、静态变量、常量
常量区中存放const修饰的全局常量和字符串常量
32.内存四区---程序执行后
栈区数据注意事项---不要返回局部变量的地址
栈区的数据由编译器管理开辟和释放
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在C++中主要利用new在堆区开辟内存
33. 引用
数据类型 &别名=原名
int a=10;
int &b=a;
备注:引用必须要初始化;引用一旦初始化后,就不可以更改了;
引用的本质在C++内部实现是一个指针常量
#include<iostream>
using namespace std;
#include<string.h>
void func(int& ref) {
ref = 100;
}
int main() {
int a = 100;
//自动转换为int* const ref=&a;指针常量是指针指向不可改,也说明为什么引用不可更改
int& ref = a;
ref = 20;//内部发现ref是引用,自动帮我们转换为 *ref=20
cout << "a:" << a<< endl;
cout << "ref: " << ref << endl;
func(a);
return 0;
}
34.常量引用
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,防止形参改变实参
35.函数的默认参数
如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
如果函数声明有默认值,函数实现的时候就不能有默认参数
占位参数:void func(int a,int){cout<<"this is func"<<endl;}
36.函数重载
作用:函数名可以相同,提高复用性
备注:同一个作用域下;函数名称相同;函数参数类型相同或者个数不同或者顺序不同
注意:引用作为重载条件;函数重载碰到函数默认参数
37.类和对象
//设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号
class Student {
public:
//类中的属性和行为 我们统一称为 成员
//属性 成员属性 成员变量
//行为 成员函数 成员方法
//属性
string name;
int id;
//行为
void setName(string name) {
name = name;
}
void setId(int id) {
id = id;
}
};
38.访问权限
公共权限 public 成员 类内可以访问 类外可以访问
保护权限 protected 成员 类内可以访问 类外不可以访问
私有权限 private 成员 类内可以访问 类外不可以访问
备注:struct默认权限为公共;class默认权限为private
39.成员属性设置为私有
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
40.构造函数和析构函数
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作
构造函数用法:类名(){}
析构函数语法:~类名(){}
//拷贝构造函数
Person(const Person &p){
age=p.age;
cout<<"拷贝构造函数"<<endl;
}
括号法中:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
41.构造函数调用规则
如果用户定义有参构造函数,C++将不会提供默认的无参构造函数,但是会提供默认拷贝
如果用户定义拷贝构造函数,C++不会再提供其他构造函数
42.深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
备注:浅拷贝带来的问题就是堆区的内存重复释放(需要利用深拷贝进行解决)
43.初始化列表
class Person {
public:
Person(int a, int b, int c) {
m_a = a;
m_b = b;
m_c = c;
}//传统初始化
int m_a;
int m_b;
int m_c;
void print() {
cout << m_a << endl;
cout << m_b << endl;
cout << m_c << endl;
}
};
int main() {
Person p(1, 2, 3);
p.print();
return 0;
}
Person(int a,int b,int c):m_a(a),m_b(b),m_c(c){}
44.类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
当其他类对象作为本类成员,构造时候先构造类对象,再构造自身
class Phone {
public:
Phone(string pname) {
m_pname = pname;
}
string m_pname;
};
class Person {
public:
Person(string name, string pname) :m_name(name),m_phone(pname)
{
}
string m_name;
Phone m_phone;
};
void test01() {
Person p("张三", "苹果MAX");
cout << p.m_name << "拿着" << p.m_phone.m_pname<< endl;
}
int main() {
test01();
system("pause");
return 0;
}
45.静态成员
静态成员就是在成员变量和成员函数前加上关键字static,成为静态成员
静态成员变量:所有对象共享同一份数据、在编译阶段分配内存、类内声明,类外初始化
静态成员函数:所有对象共享同一个函数,静态成员函数只能访问静态成员变量
#include<iostream>
using namespace std;
#include<string.h>
class Person {
public:
static int ma;
//静态成员变量也具有访问权限
private:
static int mb;//类外访问不到私有成员变量
};
int Person::ma = 40;//类内声明,类外初始化
int Person::mb = 100;
void test01() {
Person p;
cout << p.ma << endl;
Person p2;
p2.ma = 200;//共享一份数据
cout << p.ma << endl;
}
void test02() {
//静态成员变量 不属于某个对象上 所有对象都共享同一份数据
//因此静态成员变量有两种访问方式
//通过对象进行访问
Person p;
cout << p.ma << endl;
//通过类名进行访问
cout << Person::ma << endl;
}
int main() {
test02();
system("pause");
return 0;
}
class Person {
public:
static void func() {
cout << "static void func调用" << endl;
m_a = 200;//静态成员函数可以访问 静态成员变量,不可以访问非静态成员变量
}
static int m_a;//静态成员变量
};
void test01() {
Person p;
p.func();
Person::func();//所有对象共享一个函数
}
int main() {
system("pause");
return 0;
}
46.成员变量和成员函数
//成员变量和成员函数分开存储的
class Person {
int m_a;//非静态成员变量,属于类的对象上
static int m_b;//静态成员变量,不属于类对象上
void func() {//非静态成员函数 不属于类的对象上
}
static void func2(){}//静态成员函数 不属于类的对象上
};
int Person::m_b = 0;
void test01() {
Person p;
//空对象占用内存空间为:1
//C++编译器会给每一个空对象也分配一个字节空间,是为了区分空对象占内存的位置
//每个空对象也应该有一个独一无二的内存地址
cout << "size of p= " << sizeof(p) << endl;
}
void test02() {
Person p;
cout << "size of p= " << sizeof(p) << endl;
}
int main() {
test01();
system("pause");
return 0;
}
47.this指针
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
这一块代码是如何区分哪个对象调用自己的呢?
通过使用特殊的对象指针,this指针,this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针用途:①当形参和成员变量同名时,可用this指针来区分
②在类的非静态成员函数中返回对象本身,可使用return *this.
#include<iostream>
using namespace std;
#include<string.h>
class Person {
public:
Person(int age) {
//this指针指向 被调用的成员函数 所属的对象
this->age = age;
}
int age;
Person& PersonAddAge(Person &p) {
this->age += p.age;
//this指向p2的指针,而*this指向的就是p2这个对象本体
return *this;
}
};
void test01() {
Person p1(18);
cout << "p1的年龄为: " << p1.age << endl;
}
void test02() {
Person p1(10);
Person p2(10);
//链式编程思想
p2.PersonAddAge(p1).PersonAddAge(p1);
cout << "p2的年龄为: " << p2.age << endl;
}
int main() {
test02();
system("pause");
return 0;
}
48.空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
class Person {
public:
void showClassName() {
cout << "this is Person class" << endl;
}
void showPersonAge() {
if (this == NULL) {
return;
}
//报错原因是因为传入的指针为null
cout << "age= " << m_age << endl;
}
int m_age;
};
void test01() {
Person* p=NULL;
p->showClassName();
}
int main() {
test01();
system("pause");
return 0;
}
49.const修饰成员函数
成员函数后加const后我们称这个函数为常函数
常函数内不可以修饰成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:声明对象前加const称该对象为常对象;常对象只能调用常函数
class Person {
public:
//this指针的本质 是指针常量 指针的指向是不可以修改的
//const Person * const this;
//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
void showPerson() const
{
this->m_b = 100;
}
int m_a;
mutable int m_b;//特殊变量,即使在常函数中,也可以修改这个值
};
50.友元
友元的目的:让一个函数或者类 访问另一个类中私有成员
友元关键字为:friend;
友元的三种实现:全局函数做友元;类做友元;成员函数做友元
class Building {
//goodGay全局函数是Building好朋友,可以访问Building中私有成员
friend void goodGay(Building* building);
public:
Building() {
sitroom = "客厅";
bedroom = "卧室";
}
public:
string sitroom;
private:
string bedroom;
};
void goodGay(Building* building) {
cout << "好基友" << building->sitroom << endl;
cout << "好基友" << building->bedroom << endl;
}
void test01() {
Building building;
goodGay(&building);
}
class Building;
class GoodGay {
public:
GoodGay();
void visit();//参观函数,访问Building中的属性
Building* building;
};
class Building {
//GoodGay类是本类的好朋友,可以访问本类中私有成员
friend class GoodGay;
public:
Building();
public:
string sitroom;
private:
string bedroom;
};
//类外去写成员函数
Building::Building() {
sitroom = "客厅";
bedroom = "卧室";
}
GoodGay::GoodGay() {
building = new Building();
}
void GoodGay::visit() {
cout << "好基友类正在访问:" << building->sitroom << endl;
cout << "好基友类正在访问:" << building->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中私有成员
void visit2();//让visit函数不可以访问Building中私有成员
Building* building;
};
class Building {
//告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员
friend void GoodGay::visit();
public:
Building();
public:
string sitroom;
private:
string bedroom;
};
//类外去写成员函数
Building::Building() {
sitroom = "客厅";
bedroom = "卧室";
}
GoodGay::GoodGay() {
building = new Building();
}
void test01() {
GoodGay gg;
gg.visit();
}
void GoodGay::visit() {
cout << "visit函数正在访问 " << building->sitroom << endl;
cout << "visit函数正在访问 " << building->bedroom << endl;
}
void GoodGay::visit2() {
cout << "visit函数正在访问 " << building->sitroom << endl;
}
int main() {
test01();
system("pause");
return 0;
}
friend void GoodGay::visit();
51.运算符重载
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
//加号运算符重载
class Person {
public:
/*Person operator+(Person& p) {
Person temp;
temp.ma = this->ma + p.ma;
temp.mb = this->mb + p.mb;
return temp;
}*/
int ma;
int mb;
};
//全局函数重载+号
Person operator+(Person& p1, Person& p2) {
Person temp;
temp.ma = p1.ma + p2.ma;
temp.mb = p1.mb + p2.mb;
return temp;
}
void test01() {
Person p1;
p1.ma = 10;
p1.mb = 10;
Person p2;
p2.ma = 10;
p2.mb = 10;
Person p3 = p1 + p2;
cout << "p3.ma= " << p3.ma << endl;
cout << "p3.mb= " << p3.mb << endl;
}
int main() {
test01();
system("pause");
return 0;
}
//加号运算符重载
class Person {
public:
/*Person operator+(Person& p) {
Person temp;
temp.ma = this->ma + p.ma;
temp.mb = this->mb + p.mb;
return temp;
}*/
int ma;
int mb;
};
//全局函数重载+号
Person operator+(Person& p1, Person& p2) {
Person temp;
temp.ma = p1.ma + p2.ma;
temp.mb = p1.mb + p2.mb;
return temp;
}
void test01() {
Person p1;
p1.ma = 10;
p1.mb = 10;
Person p2;
p2.ma = 10;
p2.mb = 10;
Person p3 = p1 + p2;
cout << "p3.ma= " << p3.ma << endl;
cout << "p3.mb= " << p3.mb << endl;
}
int main() {
test01();
system("pause");
return 0;
}
51.继承
下级别的成员除了拥有上一级的共性,还有自己的特性
继承的好处:减少重复代码
语法:class 子类:继承方式 父类
子类:派生类 父类:基类
父类中所有非静态成员属性都会被子类继承下去
父类中私有成员属性,是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了
52.继承中构造和析构顺序
先构造父类,再构造字类,析构的顺序和构造的顺序相反
53.继承同名成员处理方式
访问字类同名成员 直接访问即可
class Base {
public:
Base() {
ma = 100;
}
int ma;
};
class Son :public Base {
public:
Son() {
ma = 200;
}
int ma;
};
void test01() {
Son s;
cout << "ma= " << s.ma << endl;
//如果通过子类对象,访问到父类中同名成员,需要加作用域
cout << "Base 下 ma= " << s.Base::ma << endl;
}
int main() {
test01();
system("pause");
return 0;
}
54.继承同名静态成员处理方式
有两种方式,一是通过对象访问,二是通过类名访问
Son::Base::ma
//第一个::代表通过类名方式访问,第二个::代表访问父类作用域下
备注:多继承语法:class 子类:继承方式 父类1,继承方式 父类2...
55.菱形继承
两个派生类继承同一个基类,又有某个类同时继承这两个派生类
继承前加virtual关键字后,变为虚继承
此时公共的父类Animal称为虚基类
56.多态
静态多态:函数地址早绑定,编译阶段确定函数地址
动态多态:函数地址晚绑定,运行阶段确定函数地址
备注:①有继承关系②子类重写父类的虚函数
动态多态的使用:父类的指针或者引用,指向子类对象
当子类重写父类的虚函数
子类中的虚函数表内部会替换成子类的虚函数地址
57.纯虚函数和抽象类
纯虚函数语法:virtual 返回值类型 函数名(参数列表)=0
抽象类特点:①无法实例化对象;
②子类必须重写抽象类的纯虚函数,否则也属于抽象类
58.虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
虚析构和纯虚析构共性:①可以解决父类指针释放子类对象②都需要有具体的函数实现
59.文件操作
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件<fstream>
写文件的步骤:①包含头文件#include<fstream>②创建流对象ofstream ofs
③打开文件 ofs.open("文件路径",打开方式)④写数据ofs<<”写入的数据“
⑤ofs.close()关闭文件
#include<fstream>
void test01() {
ofstream ofs;
ofs.open("text.txt", ios::out);
ofs << "xingming" << endl;
ofs << "xingbie" << endl;
ofs.close();
}
60.模板
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
template<typename T>
函数声明或定义
void swapInt(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
//函数模板
template<typename T>//声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
void mySwap(T &a,T &b) {
T temp = a;
a = b;
b = temp;
}
void test01() {
int a = 10;
int b = 20;
mySwap(a, b);//自动类型推导
mySwap<int>(a, b);//显示指定类型
cout << "a= " << a << endl;
cout << "b= " << b << endl;
}
普通函数模板调用时可以发生自动类型转换
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型的方式,可以发生隐式类型转换
int myAdd01(int a, int b) {
return a + b;
}
void test01() {
int a = 10;
int b = 20;
char c = 'c';//a对应97
cout << myAdd01(a, c) << endl;
}//隐式类型转换
int myAdd01(int a, int b) {
return a + b;
}
template<class T>
T myAdd02(T a,T b) {
return a + b;
}
void test01() {
int a = 10;
int b = 20;
char c = 'c';
cout << myAdd02(a, c) << endl;
cout << myAdd02<int> << endl;
}
普通函数与函数模板的调用规则
如果函数模板与普通函数都可以调用,优先调用普通函数
可以通过空模板参数列表 强制调用 函数模板
函数模板可以发生函数重载
如果函数模板可以产生更好的匹配,优先调用函数模板
void myPrint(int a,int b) {
cout << "调用的普通函数" << endl;
}
template<class T>
void myPrint(T a, T b) {
cout << "调用的模板" << endl;
myPrint(a, b);
}
void test01() {
int a = 10;
int b = 20;
myPrint<>(a, b);
}
61.模板的局限性
//模板并不是万能的,有些特定数据类型,需要用具体化方式做特殊实现
class Person {
public:
Person(string name, int age) {
this->age = age;
this->name = name;
}
public:
string name;
int age;
};
template<class T>
bool myCompare(T& a, T& b)
{
if (a == b) {
return true;
}
else
{
return false;
}
}
//利用具体化的Person版本实现代码,具体化优先调用
template<> bool myCompare(Person& p1, Person& p2) {
if (p1.name == p2.name && p1.age == p2.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);
bool ret = myCompare(p1, p2);
if (ret) {
cout << "p1==p2" << endl;
}
else {
cout << "p1!=p2" << endl;
}
}
总结:利用具体化的模板,可以解决自定义类型的通用化
学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
62.类模板
//类模板
template<class NameType,class AgeType>
class Person {
public:
Person(NameType name, AgeType age) {
this->age = age;
this->name = name;
}
void showPerson() {
cout << "name " << this->name << "age " << this->age << endl;
}
NameType name;
AgeType age;
};
void test01() {
Person<string, int> p1("孙悟空", 99);
p1.showPerson();
}
63.类模板与函数模板区别
①类模板没有自动类型推导的使用方式
②类模板在模板参数列表中可以有默认参数
64.类模板中成员函数创建时机
普通类中的成员函数一开始就可以创建
类模板中的成员函数在调用时才创建
对不同数据类型数组进行排序
//实现通用 对数组进行排序的函数
//规则 从大到小
//算法 选择
//测试char数组 int数组
template<class T>
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
template<class T>
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) {
mySwap(arr[max], arr[i]);
}
}
}
//提供打印数组模板
template<class T>
void printArray(T arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
void test01() {
char charArr[] = "badcfe";
int num = sizeof(charArr) / sizeof(char);
mySort(charArr, num);
}
class Person1 {
public:
void showPerson1(){
cout << "Person1 show" << endl;
}
};
class Person2 {
public:
void showPerson2() {
cout << "Person2 show" << endl;
}
};
template<class T>
class MyClass {
public:
T obj;
void func1() {
obj.showPerson1();
}
void func2() {
obj.showPerson2();
}
};
void test01() {
MyClass<Person1>m;
m.func1();
}//创建时机
65.类模板对象做函数参数
类模板实例化出的对象,向函数传参的方式
67.STL(标准模板库)
STL从广义上分为:容器 算法 迭代器
容器和算法之间通过迭代器进行无缝衔接
六大组件:容器 算法 迭代器 仿函数 适配器(配接器) 空间配置器
STL容器就是将运用的最广泛的一些数据结构实现出来
容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序
关联式排序:二叉树关系,各元素之间没有严格的物理上的顺序关系
算法分为质变算法和非质变算法
迭代器:提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器
种类:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器
68.vector容器
算法:for_each
迭代器:vector<int>::iterator
#include<iostream>
using namespace std;
#include<string.h>
#include<vector>
#include<algorithm>//标准算法头文件
//vector容器存放内置数据类型
void myPrint(int val) {
cout << val << endl;
}
void test01() {
//创建了一个vector容器,数组
vector<int> v;
//向容器中插入数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);吗
//通过迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin();//起始迭代器,指向容器中第一个元素
vector<int>::iterator itEnd = v.end();//结束迭代器,指向容器中最后一个元素的下一个位置
//第一种遍历方式
while (itBegin != itEnd) {
cout << *itBegin << endl;
itBegin++;
}
//第二种遍历方式
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
//第三种遍历方式 利用STL提供遍历算法
for_each(v.begin(), v.end(), myPrint);
}
69.vector容器存放自定义数据类型
class Person {
public:
Person(string name, int age) {
this->age = age;
this->name = name;
}
string name;
int age;
};
void test01() {
vector<Person>v;
Person p1("aaa", 10);
Person p2("aaa", 20);
Person p3("aaa", 30);
Person p4("aaa", 40);
Person p5("aaa", 50);
//向容器中添加数据
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
//遍历容器中的数据
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
cout << "姓名:"<< (*it).name<<"年龄:"<<(*it).age<< endl;
}
}
//存放自定义数据类型 指针
void test02() {
vector<Person*>v;
Person p1("aaa", 10);
Person p2("aaa", 20);
Person p3("aaa", 30);
Person p4("aaa", 40);
Person p5("aaa", 50);
//向容器中添加数据
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
v.push_back(&p5);
//遍历容器
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
cout << "姓名:" << (*it)->name << "年龄:" << (*it)->age << endl;
}
}
70.Vector容器嵌套容器
//容器嵌套容器,进行遍历输出
void test01() {
vector<vector<int>>v;
//创建小容器
vector<int>v1;
vector<int>v2;
vector<int>v3;
vector<int>v4;
//向小容器中添加数据
for (int i = 0; i < 4; i++) {
v1.push_back(i + 1);
v2.push_back(i + 2);
v3.push_back(i + 3);
v4.push_back(i + 4);
}
//将小容器插入到大容器中
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
//通过大容器,把所有数据遍历一遍
for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
//(*it)---容器vector<int>
for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
cout << *vit << " ";
}
cout << endl;
}
}
71.string容器
string本质上是一个类
string和char*的区别:
char *是一个指针;
string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器
void test01() {
string str1;
str1 = "hello world";
cout << "str1= " << str1 << endl;
string str2;
str2 = str1;
cout << "str2= " << str2 << endl;
string str4;
str4.assign("hello C++");
cout << "str4= " << str4<< endl;
string str5;
str5.assign("hello C++", 5);
cout << "str5= " << str5 << endl;
string str6;
str6.assign(str5);
cout << "str6= " << str6 << endl;
string str7;
str7.assign(10, 'w');
cout << "str7= " << str7 << endl;
}
72.string字符串拼接
void test01(){
string str1 = "我";
str1 += "爱玩游戏";
cout << "str1= " << str1 << endl;
str1 += ":";
cout << "str1= " << str1 << endl;
string str2 = "lol";
str1 += str2;
cout << "str1= " << str1 << endl;
string str3 = "I";
str3.append("love");
cout << "str3= " << str3 << endl;
str3.append("game abcde",4);
cout << "str3= " << str3 << endl;
//str3.append(str2);
//cout << "str3= " << str3 << endl;
str3.append(str2,4,3);//从下标4位置开始,截取3个字符,拼接到字符串末尾
cout << "str3= " << str3 << endl;
}
str3.append(str2,4,3);//从下标4位置开始,截取3个字符,拼接到字符串末尾
70.字符串的查找和替换
void test01() {
string str1 = "abcdefg";
int pos = str1.find("df");
if (pos == -1) {
cout << "未找到字符串" << endl;
}
cout << "pos= " << pos << endl;
//rfind从右往左找,find从左往右找
pos = str1.rfind("de");
cout << "pos= " << pos << endl;
}
void test02() {
string str1 = "abcdefg";
//从1号位置起3个字符,替换为“1111”
str1.replace(1, 3, "1111");
cout << "str1= " << str1 << endl;
}
备注:找不到返回-1
替换时,要指定从哪个位置起,替换多少参数
71.string字符串比较
void test01() {
string str1 = "hello";
string str2 = "hello";
if (str1.compare(str1) == 0) {
cout << "str1等于str2" << endl;
}
else if (str1.compare(str2) > 0) {
cout << "str1大于str2" << endl;
}
else {
cout << "str1小于str2" << endl;
}
}
备注:按照ASCLL码进行对比
=返回0; >返回1; <返回-1
72.字符串存取
//string字符存取
void test01() {
string str = "hello";
cout << "str= " << str << endl;
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';
cout << "str= " << str << endl;
str.at(1)='x';
cout << "str= " << str << endl;
}
73.插入和删除
//字符串的插入和删除
void test01() {
string str = "hello";
str.insert(1, "111");
cout << "str= " << str << endl;
//删除
str.erase(1, 3);
cout << "str= " << str << endl;
}
str.insert(1, "111");
str.erase(1, 3);
备注:插入和删除的起始下标都是从0开始
74.string子串
在字符串中获取想要的子串
//string求子串
void test01() {
string str = "abcdefg";
string subStr = str.substr(1, 3);
cout << "subStr = " << subStr << endl;
}
void test02() {
string email = "zhangsan@qq.com";
int pos = email.find("@");
cout << pos << endl;
string usrName = email.substr(0, pos);
cout << usrName << endl;
}
75.vector容器
vector数据结构和数组非常相似,也成为单端数组
不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展:并不是在原空间后虚继新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。
//vector容器构造
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01() {
vector<int>v1;//默认构造 无参构造
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
printVector(v1);
//通过区间进行构造
vector<int>v2(v1.begin(), v1.end());
printVector(v2);
//n个elem方式构造
vector<int>v3(10, 100);
printVector(v3);
//拷贝构造
vector<int>v4(v3);
printVector(v4);
}
76.vector赋值操作
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01() {
vector<int>v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
printVector(v1);
//赋值
vector<int>v2;
v2 = v1;
printVector(v2);
vector<int>v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
//n个elem赋值
vector<int>v4;
v4.assign(10, 100);
printVector(v4);
}
77.vector的容量和大小
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01() {
vector<int>v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
printVector(v1);
if (v1.empty()) //为真 代表容器为空
{
cout << "v1不为空" << endl;
}
else {
cout << "v1不为空" << endl;
cout << "v1的容量: " << v1.capacity() << endl;
cout << "v1的大小: " << v1.size() << endl;
}
//重新指定大小
v1.resize(15);//利用重载版本,可以指定默认填充值,参数2
printVector(v1);//如果重新指定的比原来长了,默认用填充新的位置
v1.resize(5);
printVector(v1);//如果重新指定的比原来短了,超出部分会删除掉
}
78.vector数据存取
//vector容器 数据存取
void test01() {
vector<int>v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
//利用[]方式访问数组元素
for (int i = 0; i < v1.size(); i++) {
cout << v1[i] << " ";
}
cout << endl;
//利用at方式访问元素
for (int i = 0; i < v1.size(); i++) {
cout << v1.at(i) << " ";
}
cout << endl;
//返回第一个元素
cout << "第一个元素: " << v1.front() << endl;
//获取最后一个元素
cout << "最后一个元素为: " << v1.back() << endl;
}
79.vector互换容器
实现两个容器内元素进行互换
//vector容器互换
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01() {
vector<int>v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
cout << "交换前: " << endl;
printVector(v1);
vector<int>v2;
for (int i = 10; i > 0; i--) {
v2.push_back(i);
}
printVector(v2);
cout << "交换后: " << endl;
v1.swap(v2);
printVector(v1);
printVector(v2);
}
//实际用途
//巧用swap可以收缩内存空间
void test02() {
vector<int>v;
for (int i = 0; i < 10000; i++) {
v.push_back(i);
}
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小: " << v.size() << endl;
vector<int>(v).swap(v);
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小: " << v.size() << endl;
}
80.vector预留空间
//vector预留空间
void test01() {
vector<int>v;
//利用reserve(100000)
v.reserve(100000);
int num = 0;//统计开辟次数
int* p = NULL;
for (int i = 0; i < 100000; i++) {
v.push_back(i);
if (p != &v[0]) {
p = &v[0];
num++;
}
}
cout << "num= " << num << endl;
}
如果数据量较大,可以一开始利用reserve预留空间
81.deque容器
双端数组,可以对头端进行插入删除操作
deque和vector区别:
vector对于头部的插入删除效率低,数据量越大,效率越低
deque相对而言,对头部的插入删除速度会比vector快
vector访问元素的速度会比deque快,这和两者内部实现有关。
deque内部工作原理:deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
deque容器的迭代器也是支持随机访问的
82.deque插入和删除
void printDeque(const deque<int>& d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01() {
deque<int>d1;
//尾插
d1.push_back(10);
d1.push_back(20);
//头插
d1.push_front(100);
d1.push_front(200);
printDeque(d1);
//尾删
d1.pop_back();
printDeque(d1);
//头删
d1.pop_front();
printDeque(d1);
}
void test02() {
deque<int>d1;
d1.push_back(10);
d1.push_back(20);
d1.push_front(100);
d1.push_front(200);
printDeque(d1);
//insert插入
d1.insert(d1.begin(), 2, 10000);
printDeque(d1);
//10000 10000 200 100 10 20
deque<int>d2;
d2.push_back(1);
d2.push_back(2);
d2.push_back(3);
d1.insert(d1.begin(), d2.begin(), d2.end());
printDeque(d1);
//1 2 3 10000 10000 200 100 10 20
}
void test03() {
deque<int>d1;
d1.push_back(10);
d1.push_back(20);
d1.push_front(100);
d1.push_front(200);
//删除
deque<int>::iterator it = d1.begin();
it++;
d1.erase(it);
printDeque(d1);
//按区间方式删除
d1.erase(d1.begin(), d1.end());
}
83.deque数据存取
//deque容器排序
void printDeque(const deque<int>&d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01() {
deque<int>d;
d.push_back(10);
d.push_back(20);
d.push_back(30);
d.push_front(100);
d.push_front(200);
d.push_front(300);
printDeque(d);
sort(d.begin(), d.end());
cout << "排序后:" << endl;
printDeque(d);
}
对于支持随机访问的迭代器的容器,都可以利用sort算法直接对其进行排序
84.stack容器
栈不允许有遍历行为
void test01() {
stack<int>s;
s.push(10);
s.push(20);
s.push(30);
s.push(40);
while (!s.empty())
{
cout << "栈顶元素为:" << s.top() << endl;
s.pop();
}
cout << "栈的大小:" << s.size() << endl;
}
85.queue容器
队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
86.list容器
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器。
list的优点:
采用动态存储分配,不会造成内存浪费和溢出
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
备注:list有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。
void printList(const list<int>& L) {
for (list<int>::const_iterator it = L.begin(); it != L.end();it++) {
cout << *it << " ";
}
cout << endl;
}
void test01() {
//创建list容器
list<int>L1;
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
//遍历容器
printList(L1);
//区间方式构造
list<int>L2(L1.begin(), L1.end());
printList(L1);
//n个elem
list<int>L4(10, 1000);
printList(L4);
}
87.list赋值和交换
给list容器进行赋值,以及交换list容器
push_back(elem):在容器尾部加入一个元素
pop_back():删除容器中最后一个元素
push_front(elem):在容器开头插入一个元素
pop_front():从容器开头移除第一个元素
insert(pos,elem):在pos位置插elem元素的拷贝,返回新数据的位置
insert(pos,n,elem):在pos位置插入n个elem数据,无返回值
inset(pos,beg,end):在pos位置插入[beg,end]区间的数据,无返回值
clear():移除容器的所有数据
erase():删除[beg,end]区间的数据,返回下一个数据的位置
remove(elem):删除容器中所有与elem值匹配的元素
88.list数据存取
对list容器中数据进行存取
//list容器的数据存取
void test01() {
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
//L1[0] 不可以用[]访问list容器中的元素
//L1.at(0) 不可以用at方式访问list容器中的元素
//原因是list本质链表,不是用连续线性空间存储数据,迭代器也是不支持随机访问的
cout << "第一个元素为:" << L1.front() << endl;
cout << "最后一个元素为:" << L1.back() << endl;
//验证迭代器是不支持随机访问的
list<int>::iterator it = L1.begin();
it++;//不可以it=it+1;支持双向,不支持随机访问
}
89.排序案例
//按照年龄进行升序,如果年龄相同按照身高进行降序
class Person {
public:
Person(string name, int age, int height) {
this->name = name;
this->age = age;
this->height = height;
}
string name;
int age;
int height;
};
bool comparePerson(Person& p1, Person& p2) {
//按照年龄 升序
if (p1.age == p2.age) {
//年龄相同,按照身高降序
return p1.height > p2.height;
}
else {
return p1.age < p2.age;
}
}
void test01() {
list<Person>L;
Person p1("刘备", 35, 175);
Person p2("曹操", 45, 180);
Person p3("孙权", 45, 170);
Person p4("赵云", 25, 190);
Person p5("张飞", 35, 160);
Person p6("关羽", 35, 200);
L.push_back(p1);
L.push_back(p2);
L.push_back(p3);
L.push_back(p4);
L.push_back(p5);
L.push_back(p5);
for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
cout << "姓名: " << (*it).name << "年龄:" << (*it).age << "身高: " << (*it).height << endl;
}
cout << "排序后" << endl;
L.sort(comparePerson);
for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
cout << "姓名: " << (*it).name << "年龄:" << (*it).age << "身高: " << (*it).height << endl;
}
}//指定排序规则
90.set容器和multiset容器
所有元素都会在插入时自动被排序
set不允许容器中有重复的元素
multiset允许容器中有重复的元素
//set容器构造和赋值
void printSet(set<int>& s) {
for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
cout << *it <<" ";
}
cout << endl;
}
void test01() {
set<int>s1;
//插入数据,只有inset方式
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(30);
s1.insert(40);
//遍历容器
//set容器特点:所有元素插入时候自动被排序
//set容器不允许插入重复值
printSet(s1);
//拷贝构造
set<int>s2(s1);
printSet(s2);
//赋值
set<int>s3;
s3 = s2;
printSet(s3);
}
备注:s1.erase(s1.begin());
s1.erase(30);
s1.erase(s1.begin(),s1.end());
//set容器 查找和统计
void test01() {
set<int>s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
set<int>::iterator pos = s1.find(30);
if (pos != s1.end()) {
cout << "找到元素:" << *pos << endl;
}
else {
cout << "未找到元素:" << endl;
}
}
void tese02() {
set<int>s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
//统计30的个数
int num = s1.count(30);
//对于set容器而言,结果要么是0,要么是1
cout << "num= " << num << endl;
}
91.set和multiset区别
set不可以插入重复数据,而multiset可以
set插入数据的同时会返回插入结果,表示插入是否成功
multiset不会检测数据,因此可以插入重复数据
//set容器 和multiset容器的区别
void test01() {
set<int>s;
pair<set<int>::iterator,bool> rest=s.insert(10);
if (rest.second) {
cout << "第一次插入成功" << endl;
}
else {
cout << "第一次插入失败" << endl;
}
rest = s.insert(10);
if (rest.second) {
cout << "第一次插入成功" << endl;
}
else {
cout << "第一次插入失败" << endl;
}
}
92.pair对组创建
成对出现的数据,利用对组可以返回两个数据
//pair对组的创建
void test01() {
pair<string, int>p("Tom", 20);
cout << "姓名:" << p.first << "年龄:" << p.first << endl;
//第二种方式
pair<string, int>p2 = make_pair("Ierry", 30);
cout << "姓名:" << p.first << "年龄:" << p.first << endl;
}
93.set容器排序
//set容器排序
class MyCompare{
public:
bool operator()(int v1,int v2){
return v1 > v2;
}
};
void test01() {
set<int>s1;
s1.insert(10);
s1.insert(20);
s1.insert(30);
s1.insert(40);
s1.insert(50);
for (set<int>::iterator it = s1.begin(); it!= s1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//指定排序规则为从大到小
set<int, MyCompare>s2;
s2.insert(10);
s2.insert(40);
s2.insert(20);
s2.insert(30);
s2.insert(50);
for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
94.set容器排序
//set容器排序,存放自定义数据类型
class Person{
public:
Person(string name, int age) {
this->age = age;
this->name = name;
}
string name;
int age;
};
class comparePerson{
public:
bool operator()(const Person& p1,const Person& p2) {
return p1.age > p2.age;
}
};
void test01() {
set<Person,comparePerson> s;
//创建Person对象
Person p1("刘备", 24);
Person p2("关羽", 22);
Person p3("张飞", 23);
Person p4("赵云", 24);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for (set<Person,comparePerson>::iterator it = s.begin(); it!= s.end(); it++) {
cout<<"姓名:" <<it->name<<"年龄:"<<it->age<< endl;
}
}
95.map/multimao容器
map中所有元素都是pair;
pair中第一个元素为key键值,起到索引作用,第二个元素为value实值
所有元素都会根据元素的键值自动排序
两者区别,是否允许容器中有重复的key值元素
//map容器 构造和赋值
void printMap(map<int, int>& m) {
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
cout << "key= " << (*it).first << "value= " << (*it).second << endl;
}
cout << endl;
}
void test01() {
map<int,int>m;
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(4, 40));
printMap(m);
//拷贝构造
map<int, int>m2(m);
printMap(m2);
//赋值
map<int, int>m3;
m3 = m2;
printMap(m3);
}
备注:map中所有元素都是成对出现,插入数据时候要使用对组
96.map插入和删除
//map容器插入和删除
void printMap(map<int, int>& m) {
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
cout << "key= " << it->first << "value= " << it->second << endl;
}
}
void test01() {
map<int, int>m;
//插入
//第一种
m.insert(pair<int, int>(1, 10));
//第二种
m.insert(make_pair(2, 20));
//第三种
m.insert(map<int, int>::value_type(3, 30));
//第四种
m[4] = 40;
cout << m[5] << endl;
printMap(m);
//删除
m.erase(m.begin());
printMap(m);
m.erase(3);//按照key删除
printMap(m);
m.erase(m.begin(), m.end());
printMap(m);
}
map<int,int>::iterator pos=m.find(3);
map不允许插入重复key元素,count统计而言,结果要么是0,要么是1;
multimap的count的统计可能大于1
97.map容器排序
map容器默认排序规则为 按照key值进行 从小到大排序,掌握如何改变排序规则
利用仿函数,可以改变排序规则
//map容器排序
class MyCompare {
public:
bool operator()(int v1, int v2) {
//降序
return v1 > v2;
}
};
void test01() {
map<int, int,MyCompare>m;
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(4, 40));
m.insert(pair<int, int>(5, 50));
for (map<int, int,MyCompare>::iterator it = m.begin(); it != m.end(); it++) {
cout << "key= " << it->first << "value= " << it->second << endl;
}
}
98.员工分组
//创建10名员工,放到vector中
//遍历vector容器,取出每个员工,进行随机分组
//分组后,将员工部门编号作为key,具体员工工作为value,放入到multimap容器中
//分部门现实员工信息
#define CEHUA 0
#define MEISHU 1
#define YANFA 2
class Worker {
public:
string name;
int salary;
};
void createWorker(vector<Worker>& v) {
string nameSeed = "ABCDEFGHIJ";
for (int i = 0; i < 10; i++) {
Worker worker;
worker.name = "员工";
worker.name += nameSeed[i];
worker.salary = rand() % 10000 + 10000;
//将员工放入到容器中
v.push_back(worker);
}
}
void setGroup(vector<Worker>& v, multimap<int, Worker>&m) {
for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++) {
//产生随机部门编号
int deptId = rand() % 3;
//key部门编号,value具体员工
m.insert(make_pair(deptId, *it));
}
}
oid showWorkerByGroup(multimap<int, Worker>&m) {
cout << "策划部门:" << endl;
multimap<int,Worker>::iterator pos=m.find(CEHUA);
int count = m.count(CEHUA);//统计具体人数
int index = 0;
for (; pos != m.end()&&index<count; pos++,index++) {
cout << "姓名:" << pos->second.name << "工资: " << pos->second.salary << endl;
}
}
int main() {
srand((unsigned int)time(NULL));
//创建员工
vector<Worker>vWorker;
createWorker(vWorker);
//员工分组
multimap<int, Worker>mWorker;
setGroup(vWorker, mWorker);
//分组显示员工
showWorkerByGroup(mWorker);
system("pause");
return 0;
}
99.函数对象
概念:重载函数调用操作符的类,其对象常称为函数对象;函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质:函数对象(仿函数)是一个类,不是一个函数
特点:函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
函数对象超出普通函数的概念,函数对象可以有自己的状态
函数对象可以作为参数传递
class MyAdd {
public:
int operator()(int v1, int v2) {
return v1 + v2;
}
};
//函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
void test01() {
MyAdd myAdd;
cout << myAdd(10, 10) << endl;
}
//函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint {
public:
MyPrint() {
this->count = 0;
}
void operator()(string text) {
cout << text << endl;
this->count++;
}
int count;//内部自己状态
};
void test02() {
MyPrint myPrint;
myPrint("hello world");
myPrint("hello world");
myPrint("hello world");
cout << "myPrint调用次数为: " << myPrint.count << endl;
}
//函数对象可以作为参数传递
void doPrint(MyPrint&mp,string test) {
mp(test);
}
void test03() {
MyPrint myPrint;
doPrint(myPrint, "Hello c++");
}
100.谓词
返回bool类型的仿函数成为谓词
如果operator()接受一个参数,那么叫做一元谓词
如果operator()接受两个参数,那么叫做二元谓词
class GreaterFive {
public:
bool operator()(int val) {
return val > 5;
}
};
void test01() {
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
//查找容器中有没有大于5的数字
vector<int>::iterator it=find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end()) {
cout << "未找到" << endl;
}
else {
cout << "找到了大于5 的数字为: " << *it << endl;
}
}
class MyCompare {
public:
bool operator()(int val1,int val2) {
return val1 > val2;
}
};
void test01() {
vector<int>v;
v.push_back(10);
v.push_back(40);
v.push_back(20);
v.push_back(30);
v.push_back(50);
sort(v.begin(), v.end());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
cout << endl;
//使用函数对象,改变算法策略,变为排序规则为从大到小
sort(v.begin(), v.end(), MyCompare());
cout << "---------" << endl;
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
cout << endl;
}
101.内建函数对象
算术仿函数、关系仿函数、逻辑仿函数
#include<functional>
//内建函数对象 算术仿函数
//negate 一元仿函数 取反仿函数
void test01() {
negate<int>n;
cout << n(50) << endl;
}
void test02() {
plus<int>p;
cout << p(10, 20) << endl;
}
//内建函数对象 关系仿函数
//大于 greater
void test01() {
vector<int>v;
v.push_back(10);
v.push_back(30);
v.push_back(40);
v.push_back(20);
v.push_back(50);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
cout << endl;
//降序
sort(v.begin(), v.end(), greater<int>());
}
//内建函数对象 逻辑仿函数
//大于 greater
void test01() {
vector<bool>v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(true);
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
//利用逻辑非 将容器v搬运到容器v2中,并执行取反操作
vector<bool>v2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
102.STL常用算法
算法主要是由头文件<algorithm>.<functional>.<numeric>
//常用遍历算法 for_each
//普通函数
void print01(int val) {
cout << val << " ";
}
//仿函数
class print02 {
public:
void operator()(int val) {
cout << val << " ";
}
};
void test01() {
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
for_each(v.begin(), v.end(), print01);
cout << endl;
for_each(v.begin(), v.end(), print02());
}
transform(iterator beg1,iterator end1,iterator beg2,_func);
// transform
class Transform {
public:
int operator()(int v) {
return v;
}
};
class MyPrint {
public:
void operator()(int val) {
cout << val << " ";
}
};
void test01() {
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
vector<int>vTarget;//目标容器
vTarget.resize(v.size());//目标容器,需要提前开辟空间
transform(v.begin(), v.end(), vTarget.begin(), Transform());
for_each(vTarget.begin(),vTarget.end(),MyPrint());
cout << endl;
}
103.查找算法
void test01() {
vector<int>v;
for (int i = 0; i <10; i++) {
v.push_back(i);
}
//查找容器中 是否有5这个元素
vector<int>::iterator it=find(v.begin(), v.end(), 5);
if (it == v.end()) {
cout << "没有找到" << endl;
}
else {
cout << "找到:" << *it << endl;
}
}
class Person {
public:
Person(string name, int age) {
this->mname = name;
this->mage = age;
}
bool operator==(const Person& p) {
if (this->mname == p.mname && this->mage == p.mage) {
return true;
}
else {
return false;
}
}
string mname;
int mage;
};
void test02() {
vector<Person>v;
//创建数据
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
//放入到容器中
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
vector<Person>::iterator it=find(v.begin(), v.end(), p2);
if (it == v.end()) {
cout << "没有找到" << endl;
}
else {
cout << "找到元素 姓名:" << it->mname << "年龄:" << it->mage << endl;
}
}
备注:利用find可以在容器中找指定的元素,返回值是迭代器
//常用查找算法 find_if
//查找内置数据类型
class GreaterFive {
public:
bool operator()(int val) {
return val > 5;
}
};
void test01() {
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
vector<int>::iterator it=find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end()) {
cout << "没有找到" << endl;
}
else {
cout << "找到了大于5的数字:" << *it << endl;
}
}
//自定义数据类型
class Person {
public:
Person(string name,int age) {
this->mname = name;
this->mage = age;
}
string mname;
int mage;
};
class Greater {
public:
bool operator()(Person& p) {
return p.mage > 20;
}
};
void test02() {
vector<Person>v;
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
vector<Person>::iterator it=find_if(v.begin(), v.end(),Greater());
if (it == v.end()) {
cout << "没有找到" << endl;
}
else {
cout << "找到姓名:" << it->mname << "年龄:" << it->mage << endl;
}
}
备注:adjacent_find查找相邻重复元素
//查找指定元素是否存在 binary_search
void test01() {
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
//查找容器中是否有9元素
//注意:容器必须是有序的序列
bool ret = binary_search(v.begin(), v.end(), 9);
if (ret) {
cout << "找到了元素" << endl;
}
else {
cout << "未找到" << endl;
}
}
备注:二分查找法查找效率很高,值得注意的是查找的容器中元素必须是有序序列
104.常用排序算法
//常用排序算法 random_shuffle
//统计内置数据类型
void myPrint(int val) {
cout << val << " ";
}
void test01() {
srand((unsigned int)time(NULL));
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
//利用洗牌 算法 打乱顺序
random_shuffle(v.begin(), v.end());
for_each(v.begin(), v.end(),myPrint);
cout << endl;
}
merge:两个容器元素合并,并存储到另一容器中
容器元素合并,并存储到另一容器中
两个容器必须是有序的
//常用排序算法 merge 必须是有序序列
//统计内置数据类型
class myPrint {
public:
void operator()(int val) {
cout << val << " ";
}
};
void test01() {
vector<int>v1;
vector<int>v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
v2.push_back(i + 1);
}
//目标容器
vector<int>vTarget;
//提前给目标容器分配空间
vTarget.resize(v1.size() + v2.size());
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), vTarget.end(), myPrint());
cout << endl;
}
105.常用拷贝和替换算法
//常用拷贝和替换算法 copy
void myPrint(int val) {
cout << val << " ";
}
void test01() {
vector<int>v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
vector<int>v2;
v2.resize(v.size());
copy(v.begin(), v.end(), v2.begin());
for_each(v2.begin(), v2.end(), myPrint);
cout << endl;
}
备注:利用copy算法在拷贝时,目标容器记得提前开辟空间
replace(v.begin(),v.end(),20,2000)
replace_if(v.begin(),v.end(),Greater30(),3000)
按条件查找,可以利用仿函数灵活筛选满足的条件
swap(v1,v2)交换的容器要同种类型
106.常用算术生成算法
头文件#include<numeric>
accumulate:计算容器元素累计总和
fill:向容器中添加元素
void test01() {
vector<int>v;
for (int i = 0; i < 100; i++) {
v.push_back(i);
}
int total=accumulate(v.begin(), v.end(), 0);
cout << "total= " << total << endl;
}
//常用算术生成算法 fill
void myPrint(int val) {
cout << val << endl;
}
void test01() {
vector<int>v;
v.resize(10);
//后期重新填充
fill(v.begin(),v.end(),100);
for_each(v.begin(), v.end(), myPrint);
}
107.常用集合算法
set_intersection:交集
set_union:求两个集合的并集(两个集合必须是有序序列;set_union返回值既是并集中最后一个元素的位置)
//常用集合算法 set_difference
void myPrint(int val) {
cout << val << " ";
}
void test01() {
vector<int>v1;
vector<int>v2;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
v2.push_back(i + 5);
}
//创建目标容器
vector<int>vTarget;
//给目标容器开辟空间
//最特殊的情况,两个容器没有交集,取两个容器中大的size作为目标容器开辟空间
vTarget.resize(max(v1.size(),v2.size()));
cout << "v1和v2的差集为:" << endl;
vector<int>::iterator itEnd=set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), itEnd, myPrint);
cout << endl;
}
注意:要注意谁跟谁的差集,用itEnd,不然会多输出几个值