看这里!!!本文顺序按照黑马视频所写,不仅对视频内容重点进行了分析,并且包含了黑马视频课程所有代码,代码链接已经贴在了下方(工程文件可直接运行),如果你觉得有帮助的话,可以动动发财小手,帮我点个赞哦!部分文件截图如下:
链接:https://pan.baidu.com/s/14thaue7oG1f4s5PVOmQwTQ
提取码:p1cv
目录
1丶指针
2丶内存分区模型
3丶引用
4丶函数
5丶类和对象
6丶继承
7丶多态
8丶文件操作
9丶模板
10丶STL-常用容器
11丶STL-函数对象
12丶STL-常用算法
附录丶算法
1.1 const
1.1.1常量指针
1.1.2指针常量
1.1.3修饰指针+常量
2.1程序运行前
2.1.1全局区
2.1.2代码区
2.2程序运行后
2.2.1栈区
2.2.2堆区
2.3new操作符
2.3.1new的语法
3.1引用的基本使用
3.2引用的注意事项
3.3引用做函数参数
3.4引用做函数返回值
3.5引用的本质
3.6常量引用
4.1函数默认参数
4.2函数占位参数
4.3函数重载
4.3.1函数重载概述
4.3.2函数重载注意事项
5类和对象
5.1封装
5.1.1封装的语法
5.1.2封装的权限控制
5.1.3 struct 和 calss 区别
5.1.4 成员属性设置为私有化
5.2对象的初始化和清理
5.2.1构造函数和析构函数语法
5.2.2构造函数的分类及调用
5.2.3拷贝构造函数调用
5.2.4构造函数调用规则
5.2.5深拷贝和浅拷贝
5.2.6初始化列表
5.2.7类对象作为类成员
5.2.8静态成员变量
5.2.9静态成员函数
5.3c++对象模型和this指针
5.3.1成员变量和成员函数分开存储
5.3.2this指针
5.3.3空指针调用成员函数
5.3.4const修饰成员函数
5.4友元
5.4.1全局函数做友元
5.4.2类做友元
5.4.3成员函数做友元
5.5运算符重载
5.5.1加号运算符重载
5.5.2左移运算符重载
5.5.3递增递减运算符重载
5.5.4赋值运算符重载
5.5.5关系运算符重载
5.5.6函数调用运算符重载
6继承
6.1继承的基本语法
6.2继承方式
6.3继承中的对象模型
6.4继承中构造和析构顺序
6.5继承同名成员处理方式
6.6.继承同名静态成员处理方式
6.7多继承语法
6.8菱形继承
7多态
7.1多态的基本使用
7.2多态使用的原理
7.3纯虚函数和抽象类
7.4虚析构和纯虚析构
8文件操作
8.1文本文件
8.1.1写文件
8.1.2读文件
8.2二进制文件
8.2.1写文件
8.2.2读文件
9模板
9.1模板的概念
9.2函数模板
9.2.1函数模板语法
9.2.2函数模板注意事项
9.2.3普通函数和函数模板区别
9.2.4普通函数和函数模板的调用规则
9.2.5模板的局限性
9.3类模板
9.3.1类模板语法
9.3.2类模板于函数模板区别
9.3.3类模板中成员函数创建时机
9.3.4类模板对象做函数参数
9.3.5类模板与继承
9.3.6类模板成员函数类外实现
9.3.7类模板分文件编写
9.3.8类模板与友元
10STL-常用容器
10.1STL初始
10.1.1STL的诞生
10.1.2STL基本概念
10.1.3STL六大组件
10.2STL中容器,算法,迭代器
10.2.1STL中容器,算法,迭代器概念
10.2.2vector存放内置数据类型
10.2.3vector存放自定义数据类型
10.2.4vector容器嵌套容器
10.3string容器
10.3.1string容器基本概念
10.3.2string构造函数
10.3.3string赋值操作
10.3.4string字符串拼接
10.3.5string查找和替换
10.3.6string字符串比较
10.3.7string字符读写
10.3.8string插入和删除
10.3.9string子串
10.4vector容器
10.4.1vector基本概念
10.4.2vector构造函数
10.4.3vector赋值操作
10.4.4vector容量和大小
10.4.5vector插入和删除
10.4.6vector数据读写
10.4.7vector互换容器
10.4.8vector预留空间
10.5deque容器
10.5.1deque容器基本概念
10.5.2deque构造函数
10.5.3deque赋值操作
10.5.4deque大小操作
10.5.5deque插入和和删除
10.5.6deque数据读写
10.5.7deque排序
10.6stack容器
10.6.1stack基本概念
10.6.2stack常用接口
10.7queue容器
10.7.1queue基本概念
10.7.2queue常用接口
10.8list容器
10.8.1list基本概念
10.8.2list构造函数
10.8.3list赋值和操作
10.8.4list大小操作
10.8.5list插入和删除
10.8.6list数据存取
10.8.7list反转和排序
10.9set和multiset容器
10.9.1set基本概念
10.9.2set构造和赋值
10.9.3set大小和交换
10.9.4set插入和删除
10.9.5查找和统计
10.9.6set和multiset区别
10.9.7pair队组创建
10.9.8set容器内置数据排序
10.9.9set容器自定义数据排序
10.10map和multimap容器
10.10.1map基本概念
10.10.2map构造和赋值
10.10.3map大小和交换
10.10.4map插入和删除
10.10.5map查找和统计
10.10.6map容器内置数据排序
11.1STL-函数对象
11.1.1函数对象基本概念
11.1.2函数对象使用
11.2谓词
11.2.1谓词概念
11.2.2一元谓词
11.2.3二元谓词
11.3内建函数对象
11.3.1内建函数对象意义
11.3.2算术仿函数
11.3.3关系仿函数
11.3.4逻辑仿函数
12STL常用算法
12.1常用算法基本概念
12.2常用遍历算法
12.2.1for_each
12.2.2transform
12.3常用查找算法
12.3.1find
12.3.2find_if
12.3.3adjacent_find
12.3.4binary_search
12.3.5count
12.3.6count_if
12.4常用排序算法
12.4.1sort
12.4.2random_shuffle
12.4.3merge
12.4.5reverse
12.5常用拷贝和替换算法
12.5.1copy
12.5.2replace
12.5.3replace_if
12.5.4swap
12.6常用算术生成算法
12.6.1accumulate
12.6.2fill
12.7常用集合算法
12.7.1set_intersection
12.7.2set_unin
12.7.3set_difference
1丶冒泡排序
2丶选择排序
const int * p = &a const修饰指针
指针的指向可以改
指针的内容不能改
int a = 10, b = 20;
const int* p1;//定义
//*p1 = 10;错误
p1 = &b;
int * const p = &a const修饰常量
指针的指向不能改
指针的内容可以改
int c = 10, d = 20;
int* const p2 = &c;//定义
*p2 = 30;
//p2=&d 错误
const int * const p = &a const修饰指针和常量
指针的指向不能改
指针的内容不能改
int e = 10, f = 20;
const int * const p3= &e;
//*p3 = 10;错误
//p3=&f 错误
把一个整型数据10放到堆区,然后把这个10的地址用栈区的指针来保存,并且堆区的这个整型数据10是是否存在取决于程序员。
int * p=new int (10); 把10放入堆区,并把10的地址给了指针放在栈区
new:把数据保存到堆区,并且返回该数据地址
delete:删除堆区数据,释放内存
//单个数据:
int* p = new int(10);
delete p;
//一个数组:
int* p = new int(10);
delete[] arr;
语法:数据类型 &别名=原名 例如 int &b= a
作用:给数据起另外一个名字,例如现在的b就是a,具有相同地址,并且改变b也会改变a
1.必须初始化:例如 int &b是错的
2.引用初始化之后不能改变:例如 b 是 a 的别名,那么 b 不能再是 c 的别名(可以b=c因为这是赋值操作,而不是引用操作)
名称:引用传递
作用:交换实参,实现函数的地址传递,简化指针修改实参
void swap3(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
作用:作为函数返回值返回
int &ref = test2();
cout << ref << endl;
test2() = 100;//本质是令a=100
cout << ref << endl;
注意:不要返回局部变量引用(x64可以,x86不行)
本质:引用的本质在c++内部是一个指针常量
int& ref = a;
//自动转换为 int *const ref = &a 指针常量是指针指向不能变,也就是引用不能变
func(a)
void func(int& ref)
//转为 int *const ref=&a;
{
ref = 100;//ref是引用,转为 *ref=100
}
作用:防止形参修改实参的值
void showValue(const int &val)
{
//val = 100;//不能修改 val 的值
cout << val << endl;
}
作用:给函数形参赋值一个初始值
注意事项:(1)如果有默认值,且有形参,则按形参值处理
(2)某个位置有默认形参时,后面参数都必须有形参默认值
(3)如果声明有默认参数值,则函数实现不能有默认参数值。声明和实现只能有一个有默认值
func(10,20)
int func(int a,int b=10,int c=30)
{
return a + b + c;
}
语法:返回值类型 函数名 (数据类型){}
作用:意义不大!!!
注意事项:占位参数也可以有默认值:void func(int a,int=10)
func(20,10);
void func(int a,int)
{
cout << "这是函数调用" << endl;
}
作用:函数名可以相同,提高重复性
条件:(1)同一个作用域下(比如都不在main里面)
(2)函数名称相同
(3)函数参数 类型不同 或者 个数不同 或者 顺序不同
注意:函数返回值不能当作条件:比如 void func(int a)和 int func(int a)不能同时存在
void func()
void func(int a)
void func(double a)
void func(int a,double b)
void func(double a, int b)
1.引用作为函数重载条件。加不加 const 的结果是不一样的
2.函数重载遇到函数默认参数:尽量避免
//引用作为函数重载条件
int a = 10;
func(a);
const int b = 20;
func(b);
void func(int &a)
{
cout << "这是func1调用" << endl;
}
void func(const int& a)
{
cout << "这是func2调用" << endl;
}
//函数重载遇到函数默认参数
func(10)//错误
func(10,20)//正确
void func(int a,int b=10)
{
cout << "这是func3调用" << endl;
}
void func(int a)
{
cout << "这是func4调用" << endl;
}
c++面向对象的三大特性:封装,继承,多态
人作为对象,其属性有:姓名,身高,体重,其行为有走,吃饭,睡觉
成员:包括类中的属性和行为
(1)属性:成员属性=成员变量
(2)行为:成员函数=成员方法
类:一个类中可以包含另外一个类
意义:(1)将属性和行为作为一个整体,表现生活中的事物
(2)将属性和行为加以权限控制
语法:class 类名 {权限,属性,行为};
//代表设计一个类,后面接名字
class Circle
{
//访问权限:公告权限
public:
//属性:半径
int m_r;
//行为:获取圆的周长
double caclcuateZC()
{
return 2 * PI * m_r;
}
};
//通过圆类创建具体(圆)对象
Circle c1;
//给圆对象的属性进行赋值
c1.m_r = 10;
cout << "圆的周长:" << c1.caclcuateZC() << endl;
公共权限 public ,类内可以访问,类外可以
保护权限 protected ,类内可以访问,类外不行,儿子可以访问父亲保护内容
私有权限 private ,类内可以访问,类外不行,儿子不能访问父亲内容注意事项:对于属性和方法,都要设置权限,这时的调用方式如上
class Person
{
public:
string m_name;
protected:
string m_car;
private:
int m_password;
public:
void func()
{
m_name = "张三";
m_car = "拖拉机";
m_password = 123456;
}
};
struct 默认权限为公告,所以类外能访问
calss 默认权限为私有,所以类外不能访问
好处:自己控制读写权限
class Person
{
public://在类里面设置一个公告接口,来设置调用私有属性的值
//写姓名
void setName(string name)
{
m_name = name;
}
//读姓名
string getName()
{
return m_name;
}
private://在类里面设置属性为私有
string m_name;//可读可写
};
对于数据的初始化以及销毁之前的清理数据操作
构造函数:初始化设置,编译器自动调用
析构函数:对象销毁前系统自动调用,执行清理工作
注意事项:如果自己不写这些函数,编译器会提供这些函数(空实现,空代码)
class Person
{
public:
//1,构造函数
Person()
{
cout << "Person构造函数的调用" << endl;
}
//2,析构函数
~Person()
{
cout << "Person析构函数的调用" << endl;
}
};
分类:
按参数分类:无参(默认方式) 有参
按类型分类:普通 拷贝
调用:1.括号法 2.显示法 3.隐式转换法
匿名对象:Person p1 = Person(10)里面的Person(10)
注意事项:(1)匿名对象在当前执行结束后,系统立刻回收
(2)不能用拷贝函数初始化匿名对象
class Person
{
public:
//1,构造函数无参
Person()
{
cout << "Person无参构造函数的调用" << endl;
}
//1,构造函数有参
Person(int a)
{
m_age = a;
cout << "Person有参构造函数的调用" << endl;
}
//拷贝构造
Person(const Person &p)
{
m_age = p.m_age;
cout << "Person拷贝构造函数的调用" << endl;
}
//2,析构函数
~Person()
{
cout << "Person析构函数的调用" << endl;
}
int m_age;
};
//括号法
Person p;//无参 默认方式
Person p2(10);//有参
Person p3(p2);//拷贝
//显示法
Person p1;//无参
Person p2 = Person(10);//有参
Person p3 = Person(p2);//拷贝
//隐式转换法
Person p4 = 10;//相当于写了 Person p4 = Person(10)
Person p5 = p4;
通常有三种情况:(1)使用一个已经创建完毕的对象初始化一个新对象
(2)值传递方式给函数参数传值
(3)值方式返回局部对象
void test1()
{
Person p1(20);
Person p2(p1);
cout << "pw的年龄:" << p2.m_age << endl;
}
void doWork(Person p)
{
}
void test2()
{
Person p;
doWork(p);
}
Person doWork2()
{
Person p3;
return p3;
}
void test3()
{
Person p = doWork2();
}
默认情况:C++会提供 默认构造(空函数),有参构造(空函数),拷贝构造(值传递)
规则:(1)如果用户定义有参,系统不再提供无参(无法调用),但提供拷贝(可调用)
(2)如果用户定义拷贝,系统不再提供 无参 和 有参(都无法调用)
浅拷贝:简单的赋值拷贝(默认方式)
深拷贝:在堆区重新申请空间,进行拷贝
注意事项:(1)因为浅拷贝是直接拷贝了之前的数据,如果之前数据中有堆区的指针,那么就会导致存在两个指针指向一个地址,而在析构函数 先进后出 释放堆区空间原则下,会导致重复释放堆区空间。
(2)深拷贝原理:对于拷贝数据指向的堆区会重新复制一份堆区,并且创建新的指针指向不同堆区(但是内容一样),要实现深拷贝需要自己写拷贝构造函数,并且加入析构函数判断释放空间。
总结:如果有属性在堆区开辟,那要自己写拷贝构造函数
//深拷贝
Person(const Person& p)
{
cout << "Person拷贝构造函数的调用" << endl;
m_age = p.m_age;
//m_height = p.m_height;编译器默认实现
m_height = new int(*p.m_height);//重新开辟一个堆区空间存放
}
~Person()
{
//将堆区数据进行释放
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "Person析构函数的调用" << endl;
}
语法:构造函数():属性1(值1),属性2(值2){}
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 test2()
{
Person p1(10, 200, 30);
}
构造:先构造对象,再构造自身
析构:先释放自身,再释放对象
解释:当一个类作为另外一个类的成员时,先构造该成员再构造该类==先有了人的零件才能有完整的人,先构造对象,再构造自身。。析构函数先退出自身,再退出对象
语法:static int m_A(类内)int Person::m_A=100(类外)
特点:(1)所有对象共享这份数据
(2)编译阶段分配内存
(3)类内声明类外初始化
访问方式:(1)通过对象-> Person p; cout << p.m_A << endl;
(2)通过类名-> cout << Person::m_A << endl;
注意事项:如果类中的成员是私有权限private,则不能用 “通过类名” 方式访问
class Person
{
public:
static int m_A;//所有对象共享这份数据,编译阶段分配内存,类内声明类外初始化
//访问权限
private:
static int m_B;
};
int Person::m_A=100;
int Person::m_B = 300;
void test2()
{
//通过对象访问
Person p;
cout << p.m_A << endl;
//通过类名访问
cout << Person::m_A << endl;
//cout << Person::m_B << endl;因为B是私有,所以类外不能访问
}
int main()
{
test2();
system("pause");
return 0;
}
语法:static void func2()
特点:(1)所有对象共享同一函数,类似通过类名访问时,可以不需要类名,所以说共享
(2)静态成员函数只能访问静态成员变量,
访问方式:(1)通过对象-> Person p; p1.func();
(2)通过类名-> Person::func();
注意事项:如果类中的成员是私有权限private,则不能用 “通过类名” 方式访问
class Person
{
public:
static void func()
{
m_A = 100;//静态成员函数访问静态成员变量
//m_B = 200;非静态成员不能被静态函数访问,因为它不知道这是对象p1的还是对象p2的
cout << "static void func()的调用" << endl;
}
static int m_A;
int m_B;
private:
static void func2()
{
cout << "static void func2()的调用" << endl;
}
};
int Person:: m_A=10;
void test1()
{
//1通过对象访问
Person p1;
p1.func();
//2通过类名访问
Person::func();
//Person::func2(); 因为func2是私有,所以类外不能访问
}
int main()
{
test1();
system("pause");
return 0;
}
非静态成员:属于类对象
静态成员,非静态成员函数,静态成员函数:不属于类对象
注意事项:(1)空对象,则分配一个字节
(2)在判断属不属于类对象时,会影响该对象在内存中分配的字节。例如,当 类 里面有 两个 int 和一个 static int 时,该对象占 2*4 个字节
class Person
{
int m_A;//非静态成员属于类的对象上
static int m_B;//静态成员不属于类的对象上
void func() {}//非静态成员函数不属于类的对象上
static void func2() {}//静态成员函数不属于类的对象上
};
this指针作用:(1)当形参和成员变量同名时,可以用this区分
(2) *this 返回对象本身
注意事项:(1)利用 *this 可以实现 链式编程 。因为返回值是本体,那么可以不断用本体继续调用函数
Person& PersonAddAge(Person &p)
{
this->age += p.age;//也可以命名规范避免
return *this;//返回对象本身
}
void test2()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);//链式编程
//这里会使 p2 的年纪重复加 4 次 ,因为每次的 p2.PersonAddAge(p1) 返回值又是 p2,所以
//可以继续调用
}
注意事项:(1)空指针可以正常访问没有this的成员函数
(2)空指针对于有this的成员函数,需要加上判断也可以使用
class Person
{
public:
void shwClassName()
{
cout << "this is Person class" << endl;//可以正常访问
}
void showPersonAge()
{
//报错原因是因为传入的指针为 NULL
if(this == NULL)
{
return ;
}
cout << "age = " << m_Age << endl;
}
int m_Age;
};
void test1()
{
Person* p =NULL;
p->showPersonAge();
p->shwClassName();//可以正常访问
}
常函数:(1)成员函数 后 加入 const ,该函数为 常函数
(2)常函数内不可以修改成员属性
(3)成员属性若声明 mutable 关键字,则在常函数中下可以修改
常对象:(1)声明对象 前 加入 const 称为 常对象
(2)常对象只能调用常函数
class Person
{
public:
//zhis指针的本质: 指针常量:指针的指向不能修改
// Person * const this;
void showPerson1()
{
this->m_A = 100;//正确,因为指针常量可以改变赋值
//this = NULL 错误,因为指针常量不能改变方向
}
// const Person * const this成员函数后加 const 修饰的是 this 指向,让指针指向的值不能改变
void showPerson2() const
{
this->m_B = 100;//加入 mutable 之后才可以修改
//this->m_A = 100;//错误,因为加入 const 不能改变赋值
//this = NULL 错误,因为不能改变方向
}
void func()
{
m_A = 100;
}
int m_A;
mutable int m_B;//特殊变量,即使在常函数中也可以修改
};
//常对象
void test1()
{
const Person p;
//p.m_A = 100; p 为常对象,不能修改
p.m_B = 100;// m_B 在常对象下也可以修改
//常对象只能调用常函数
p.showPerson2();
//p.func 常对象无法调用 非常函数
}
利用 友元关键字 friend 可以访问类中的私有成员
作用:为了使全局函数可以访问私有类
语法:在 class 类中 首行 写上 friend + 函数声明;friend void goodGay(Building* building);
class Building
{
friend void goodGay(Building* building);
public:
string m_SittingRomm;//客厅
private:
string m_BedRoom;//卧室
public:
Building()
{
m_SittingRomm = "客厅";
m_BedRoom = "卧室";
}
};
//全局函数
void goodGay(Building *building)
{
cout << "好基友全局函数在访问:" << building->m_SittingRomm << endl;
cout << "好基友全局函数在访问:" << building->m_BedRoom << endl;
}
void test1()
{
Building building;
goodGay(&building);
}
作用:为了使类可以访问其他私有类
语法:在 class 类中 首行 写上 friend + 类声明;friend class GoodGay;
class Building
{
friend class GoodGay;
public:
Building();
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
class GoodGay
{
public:
GoodGay();
void visit();//参观函数 访问 Building 中的属性
Building* building;
};
//类外写成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
//创建一个建筑物对象
building = new Building;
}
void GoodGay::visit()
{
cout << "好基友类正在访问:" << building->m_SittingRoom << endl;
cout << "好基友类正在访问:" << building->m_BedRoom << endl;
}
void test1()
{
GoodGay gg;
gg.visit();
}
函数实现流程:(1)test1()->生成 GoodGay类的对象gg,(2)gg中包括 GoodGay函数方法,会给 gg的属性Building* 类型指针building 赋初值 为Building的类型指针,(3)此时 gg具有 Building 类的属性 客厅和卧室。(4)gg会调用 building类中的方法给客厅和卧室赋初值(5)此时 gg所有属性已经赋值成功,开始调用 gg.visit()。(6)由于加入了 friend,所以可以访问私有,输出结果
作用:为了使类可以访问其他私有类
语法:在 class 类中 首行 写上 friend + 所属区域::成员函数声明;
friend void GoodGay::visit1();
注意事项:根据你的test函数,按顺序写类的定义,但要在开始写上其他类的声明
class Building;//!!!需要先声明
class GoodGay
{
public:
GoodGay();
void visit1();//让visit1函数访问Building中私有成员
void visit2();//让visit2函数不能访问Building中私有成员
Building* building;
};
class Building
{
friend void GoodGay::visit1();
public:
Building();
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
building = new Building;
}
void GoodGay::visit1()
{
cout << "visit1 函数访问:" << building->m_SittingRoom << endl;
cout << "visit1 函数访问:" << building->m_BedRoom << endl;
}
void GoodGay::visit2()
{
cout << "visit2 函数访问:" << building->m_SittingRoom << endl;
//cout << "visit2 函数访问:" << building->m_BedRoom << endl;无法访问,因为没有 friend
}
void test1()
{
GoodGay gg;
gg.visit1();
gg.visit2();
}
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同数据类型
方法:(1)成员函数重载
(2)全局函数重载(还可以进行函数重载,也就是相同名字不同输入参数)
目的:实现各种类型数据相加,例如 class + int,class +class
注意事项:成员函数重载和全局函数重载只能存在一个
#include
using namespace std;
class Person
{
public:
int m_A;
int m_B;
//1,成员函数重载 Person p3=p1.operator+(p2)
//Person operator+(Person& p)
//{
// Person temp;
// temp.m_A = this->m_A + p.m_A;
// temp.m_B = this->m_B + p.m_B;
// return temp;
//}
};
//2,全局函数重载 Person p3=operator+(p1,p2)
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
//函数重载版本 person + int
Person operator+(Person& p1, int num)
{
Person temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
void test1()
{
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;// person + person
cout << p3.m_A << ' ' << p3.m_B << endl;
Person p4 = p1 + 100;// person + int
cout << p4.m_A << ' ' << p4.m_B << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
方法:全局函数重载
作用:输出任意类型数据,例如 cout<
#include
using namespace std;
class Person
{
friend ostream& operator<<(ostream& cout, Person p);
public:
Person(int a,int b)
{
m_A = a;
m_B = b;
}
private:
int m_A;
int m_B;
//1,成员函数重载左移运算符,不使用这个方法,因为 p.operator<<(cout) = p<
前置递增:返回引用。先++,再返回。
后置递增:返回值。先返回,再++。
注意事项:(1)前置递增返回引用是为了让每次都对同一个值操作
(2)后置递增返回值是因为返回的是局部变量,而局部变量在函数结束就消失,所以不能返回这个局部变量的引用
(3)后置采用了 int 占位实现,否则前置后置函数重名
#include
using namespace std;
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 0;
}
//重载 前置++运算符,,返回引用是为了一直对一个数据进行控制
MyInteger& operator++()
{
//先进行++运算
m_Num++;
//再返回
return *this;
}
//重载 后置++运算符
//void operator++(int) int代表占位参数,用于区分前置和后置递增
MyInteger operator++(int)//返回值,不能引用,因为temp这个局部对象会在当前函数执行结束就没了
{
//先记录当时结果
MyInteger temp = *this;
//后 递增
m_Num++;
//最后返回
return temp;
}
private:
int m_Num;
};
ostream& operator<<(ostream& cout, MyInteger myint)
{
cout << myint.m_Num;
return cout;
}
void test1()//前置
{
MyInteger myint;
cout << ++(++myint) << endl;
cout << myint << endl;
}
void test2()//后置
{
MyInteger myint;
cout << myint++ << endl;
cout << myint << endl;
}
int main()
{
test2();
system("pause");
return 0;
}
#include
using namespace std;
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 1;
}
MyInteger& operator--()//前置
{
m_Num--;
return *this;
}
MyInteger operator--(int)
{
MyInteger temp = *this;
m_Num--;
return temp;
}
private:
int m_Num;
};
ostream& operator<<(ostream& cout, MyInteger myint)
{
cout << myint.m_Num ;
return cout;
}
void test1()//前置
{
MyInteger myint;
cout << --myint << endl;
cout << myint << endl;
}
void test2()//后置
{
MyInteger myint;
cout << myint-- << endl;
cout << myint << endl;
}
int main()
{
test2();
system("pause");
return 0;
}
作用:因为系统默认提供浅拷贝,为了防止析构函数报错,所以写入深拷贝。
注意事项:(1)为了实现 连= ,赋值函数需要返回其本身
(2)赋值函数需要先对堆区数据释放,再进行拷贝
#include
using namespace std;
class Person
{
public:
Person(int age)
{
m_Age=new int(age);
}
Person& operator=(Person& p)
{
//应该先判断是否有属性在堆区,如果有先释放,然后再深拷贝
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(*p.m_Age);
return *this;
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
int *m_Age;
};
void test1()
{
Person p1(18);
Person p2(20);
Person p3(22);
p3=p2 = p1;//赋值操作
cout << *p1.m_Age << endl;
cout << *p2.m_Age << endl;
cout << *p3.m_Age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
作用:对于自定义数据类型进行判断 是否相等
#include
using namespace std;
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
//重载关系运算符
bool operator==(Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
bool operator!=(Person& p)
{
if (this->m_Name != p.m_Name || this->m_Age != p.m_Age)
{
return true;
}
return false;
}
};
void test1()
{
Person p1("刘家森", 22);
Person p2("罗灿灿", 21);
//Person p2("刘家森", 22);
if (p1 == p2)
{
cout << "p1 p2相等" << endl;
}
if(p1!=p2)
{
cout << "p1 p2不相等" << endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
仿函数:重载()的函数
匿名函数对象:没有名字+仿函数
注意事项:仿函数类型灵活
#include
using namespace std;
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string test)
{
cout << test << endl;
}
};
class MyAdd
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};
void MyPrint2(string test)
{
cout <
下级成员拥有上一级的共性,并且还有自己的特性,这时通过继承,可以减少代码重复度
语法: calss 子类 :继承方式 父类
注意事项:(1)子类也叫派生类
(2)父类也叫基类
#include
using namespace std;
//普通实现页面
class Java
{
public:
void header()
{
cout << "首页,公开课,登陆,注册。。。(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心,交流合作,站内地图。。。(公共底部)" << endl;
}
void left()
{
cout << "Java,python,C++...(公共分类列表)" << endl;
}
void content()
{
cout << "java学科" << endl;
}
};
class Python
{
public:
void header()
{
cout << "首页,公开课,登陆,注册。。。(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心,交流合作,站内地图。。。(公共底部)" << endl;
}
void left()
{
cout << "Java,python,C++...(公共分类列表)" << endl;
}
void content()
{
cout << "python学科" << endl;
}
};
class CPP
{
public:
void header()
{
cout << "首页,公开课,登陆,注册。。。(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心,交流合作,站内地图。。。(公共底部)" << endl;
}
void left()
{
cout << "Java,python,C++...(公共分类列表)" << endl;
}
void content()
{
cout << "c++学科" << endl;
}
};
void test1()
{
cout << "Java下载视频界面如下: " << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "------------------------------------" << endl;
cout << "python下载视频界面如下: " << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "------------------------------------" << endl;
cout << "c++下载视频界面如下: " << endl;
CPP cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
//继承实现
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;
}
};
class Python :public BasePage
{
public:
void content()
{
cout << "python学科" << endl;
}
};
class CPP :public BasePage
{
public:
void content()
{
cout << "c++学科" << endl;
}
};
void test1()
{
cout << "Java下载视频界面如下: " << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "------------------------------------" << endl;
cout << "python下载视频界面如下: " << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "------------------------------------" << endl;
cout << "c++下载视频界面如下: " << endl;
CPP cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
int main()
{
test1();
system("pause");
return 0;
}
继承方式:公共继承,保护继承,私有继承
注意事项:(1)三种继承方式都无法继承父类的私有权限
(2)公共继承得到的和之前父类权限一样
(3)保护继承得到的全部为保护权限
(4)私有继承得到的全部为私有权限
#include
using namespace std;
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 : public Base1
{
public:
void func()
{
m_A = 10;//父类中的公共权限到了子类还是公告权限
m_B = 20;//父类中的保护权限到了子类还是保护权限
//m_C = 30;父类中的私有权限不能访问
}
};
class Son2 : protected Base1
{
public:
void func()
{
m_A = 10;//父类中的公共权限到了子类还是保护权限
m_B = 20;//父类中的保护权限到了子类还是保护权限
//m_C = 30;父类中的私有权限不能访问
}
};
class Son3 : private Base1
{
public:
void func()
{
m_A = 10;//父类中的公共权限到了子类还是私有权限
m_B = 20;//父类中的保护权限到了子类还是私有权限
//m_C = 30;父类中的私有权限不能访问
}
};
class GrandSon3 : private Son3
{
public:
void func()
{
//m_A = 10;//父类中的私有权限到了子类不能访问
//m_B = 20;//父类中的私有权限到了子类不能访问
}
};
void test1()
{
Son1 s1;
s1.m_A = 100;//公告权限在类内类外都可以访问
//s1.m_B = 100;保护权限在类内可以访问,类外不能
}
void test2()
{
Son2 s2;
//s2.m_A = 100; //保护权限在类内可以访问,类外不能
}
void test3()
{
Son3 s3;
//s3.m_A = 100;//私有权限在类内可以访问,类外不能
}
int main()
{
system("pause");
return 0;
}
注意事项:(1)父类所有对象都会传递给子类,只是私有属性不能访问
(2)利用开发人员工具查看对象模型 1.找到文件
2. cl /d1 reportSingleClassLayout类名 文件名
#include
using namespace std;
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 : public Base
{
public:
int m_D;
};
//利用开发人员工具查看对象模型
//1.找到文件
//2. cl /d1 reportSingleClassLayout类名 文件名
void test1()
{
//父类中所有非静态成员属性都会被子类继承,私有成员被继承,但无法访问
cout << "siezof son: " << sizeof(Son1) << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
子类继承父类之后,也会有父类的构造函数和析构函数
注意事项:(1)构造函数顺序:先父类,再子类
(2)析构函数顺序:先子类,后父类(先进后出原则)
#include
using namespace std;
class Base
{
public:
Base()
{
cout << "Base构造函数" << endl;
}
~Base()
{
cout << "Base析构函数" << endl;
}
};
class Son1 : public Base
{
public:
Son1()
{
cout << "Son1构造函数" << endl;
}
~Son1()
{
cout << "Son1析构函数" << endl;
}
};
void test1()
{
//Base b;
Son1 s1;
}
int main()
{
test1();
system("pause");
return 0;
}
作用:解决子类和父类同名成员问题
注意事项:(1)子类同名成员:直接访问 s1.m_A (属性) s2.func()(方法)
(2)父类同名成员:加作用域 s1.Base::m_A (属性)s2.Base::func()(方法)
(3)如果子类中出现和父类同名函数,子类函数会隐藏父类所以同名(包括重载)函数
#include
using namespace std;
class Base
{
public:
Base()
{
m_A = 100;
}
void func()
{
cout << "Base函数调用" << endl;
}
void func(int a)
{
cout << "Baseint函数调用" << endl;
}
int m_A;
};
class Son1 : public Base
{
public:
Son1()
{
m_A = 200;
}
void func()
{
cout << "son函数调用" << endl;
}
int m_A;
};
void test1()//同名成员属性
{
Son1 s1;
cout <<"son:"<< s1.m_A << endl;//直接访问是访问子类的
cout <<"base"<< s1.Base::m_A<< endl;//加作用域是访问父类的
}
//如果子类中出现和父类同名函数,子类函数会隐藏父类所以同名(包括重载)函数
void test2()//同名成员方法
{
Son1 s2;
s2.func();//直接访问是访问子类的
s2.Base::func();//加作用域是访问父类的
s2.Base::func(100);//加作用域是访问父类的
}
int main()
{
test2();
system("pause");
return 0;
}
作用:解决子类和父类静态同名成员问题
方式:(1)通过对象访问:s1.m_A (属性) s1.func();(方法)
(2)通过类名访问:Son1::m_A (属性) Son1::Base::func();(方法)
注意事项:(1)子类同名成员:直接访问 s1.m_A
(2)父类同名成员:加作用域 s1.Base::m_A
(3)如果子类中出现和父类同名函数,子类函数会隐藏父类所以同名(包括重载)函数
#include
using namespace std;
class Base
{
public:
static int m_A;
static void func()
{
cout << "Base下" << endl;
}
static void func(int a)
{
cout << "Baseint下" << endl;
}
};
int Base::m_A = 100;
class Son1 : public Base
{
public:
static int m_A;
static void func()
{
cout << "Son1下" << endl;
}
};
int Son1::m_A = 200;
//同名静态成员属性
void test1()
{
//1,通过对象访问静态成员
Son1 s1;
cout << "通过对象访问" << endl;
cout << s1.m_A << endl;
cout << s1.Base::m_A << endl;
//2,通过类名访问静态成员
cout << "通过类名访问" << endl;
cout << Son1::m_A << endl;
//第一个::代表通过类名访问,第二个::代表访问父类作用域下
cout << Son1::Base::m_A << endl;
}
//同名静态成员函数
void test2()
{
//1,通过对象访问
Son1 s1;
s1.func();
s1.Base::func();
//2,通过类名访问
Son1::func();
Son1::Base::func();
Son1::Base::func(100);
}
int main()
{
test2();
system("pause");
return 0;
}
作用:一个类继承多个类
语法:class 子类 : 继承方式 父类1 , 继承方式 父类2
注意事项:(1)父类成员同名解决办法:加作用域
#include
using namespace std;
class Base1
{
public:
Base1()
{
m_A = 100;
}
int m_A;
};
class Base2
{
public:
Base2()
{
m_A = 200;
}
int m_A;
};
class Son : public Base1, public Base2
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test1()
{
Son s;
cout << "sizeof: " << sizeof(Son) << endl;
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
概念:两个派生类共同继承一个基类,并且这两个派生类被一个派生类继承,这种叫零次菱形继承或砖石继承
虚继承:在继承方式之前加 virtual。本质是进行指针调用
注意事项:(1)由于数据继承了两个,但是只需要一个,解决办法加 作用域
(2)如果只要一份数据,则可以采用 虚继承
#include
using namespace std;
//动物类
class Animal
{
public:
int m_Age;
};
//利用虚继承,解决菱形继承问题,在继承方式之前加 virtual
// 虚继承
// vbptr:虚基类指针
//羊类
class Sheep : virtual public Animal
{
};
//驼类
class Tuo : virtual public Animal
{
};
//羊驼类
class SheepTuo : public Sheep, public Tuo
{
};
void test1()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//当出现菱形继承的时候,有两个父类拥有相同数据时,需要加作用域
cout << st.Sheep::m_Age << endl;
cout << st.Tuo::m_Age << endl;
cout << st.m_Age << endl;
//菱形继承数据有两份,导致资源浪费
}
int main()
{
test1();
system("pause");
return 0;
}
分类:(1)静态多态:函数重载 和 运算符重载属于静态,复用函数名
(2)动态多态:派生类 和虚函数实现运行时多态
区别:静态多态是函数早绑定,在编译阶段确定。动态多态是函数晚绑定,运行阶段确定
作用:能够根据不同类确定调用相同名字的函数
动态多态的条件:(1)有继承关系。
(2)子类重写父类虚函数,父类的函数加 virtual,子类加不加无所谓。
动态多态使用:父类的指针或者引用,执行子类对象虚函数:在函数前加 virtual
重写:函数所有东西一样
#include
using namespace std;
class Animal
{
public:
virtual void speak()//虚函数
{
cout << "动物在说话" << endl;
}
};
class Cat : public Animal
{
public:
void speak()
{
cout << "小猫在喵喵喵" << endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout << "小狗在汪汪汪" << endl;
}
};
//执行说话的函数
//地址早绑定,在编译阶段就确定函数地址
//如果想让 猫 说话,这个函数地址不能提前绑定,需要在运行时绑定=地址软绑定
//动态多态的条件:(1)有继承关系。(2)子类重写父类虚函数,父类的函数加 virtual,子类加不加无所谓。
//动态多态使用:(1)父类的指针或者引用,执行子类对象
void doSpeak(Animal& animal) //Animal &animal = cat,,,,父类引用可以传子类
{
animal.speak();
}
void test1()
{
Cat cat;
Dog dog;
Animal animal;
doSpeak(animal);
doSpeak(dog);
doSpeak(cat);
}
int main()
{
test1();
system("pause");
return 0;
}
原理:当父类的函数为虚函数时,此时父类中是一个 虚函数指针 指向该 父类虚函数。此外,对于子类中的同名函数,子类中也是一个虚函数指针,不过指向的是 子类虚函数。因为子类中的虚函数表覆盖了父类的虚函数表。
纯虚函数:虚函数中无实际调用 virtual void func() = 0;
抽象类:类中只要有一个纯虚函数,这个类叫抽象类
注意事项:(1)抽象类无法实例化对象
(2)抽象类子类,必须要重写父类中的纯虚函数,否则该子类也为抽象类
#include
using namespace std;
//纯虚函数和抽象类
class Base
{
public:
//只要有一个纯虚函数,这个类叫抽象类
//1,无法实例化对象
//2,抽象类子类,必须要重写父类中的纯虚函数,否则该子类也为抽象类
virtual void func() = 0;//纯虚函数语法
};
class Son : public Base
{
public:
};
class Daughter : public Base
{
public:
virtual void func()
{
cout << "调用" << endl;
}
};
void test1()
{
//Base b;//纯虚函数无法实例化对象
//new Base//纯虚函数无法实例化对象
//Son s;//抽象类子类,必须要重写父类中的纯虚函
}
void test2()
{
//对象访问
Daughter d;//子类重写了父类纯虚函数
d.func();
//指针访问
Base* base = new Daughter;
base->func();
delete base;
}
int main()
{
test2();
system("pause");
return 0;
}
虚析构:语法: virtual ~类名(){}
纯虚析构:语法 virtual ~类名()=0; 作用域::~类名(){}
共性:(1)虚析构和纯虚析构解决父类指针释放子类对象
(2)都需要具体函数实现
区别:纯虚析构属于抽象类,无法实例化对象
注意事项:如果子类中没有堆区数据,可以不写虚析构和纯虚析构
#include
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数调用" << endl;
}
//利用虚析构解决 父类指针释放子类对象时不干净问题
//virtual ~Animal()
//{
// cout << "Animal的析构函数调用" << endl;
//}
virtual ~Animal() = 0;//纯虚析构:1,需要声明,2,需要实现。有了纯虚析构之后,该类也为抽象类,无法实例化对象
virtual void speak() = 0;
};
Animal::~Animal()
{
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()
{
if (m_Name != NULL)
{
cout << "cat析构调用" << endl;
delete m_Name;
m_Name = NULL;
}
}
string *m_Name;
};
void test1()
{
Animal* animal = new Cat("灿灿");
animal->speak();
//父类指针在析构时,不会调用子类析构
delete animal;
}
int main()
{
test1();
system("pause");
return 0;
}
作用:程序运行时的数据都属于临时数据,程序运行结束时会被释放。通过文件可以将数据持久化。
分类:(1)文本文件,以ASCALL码存储
(2)二进制文件,以二进制形式存储
三大类:(1)写操作---ofstream
(2)读操作---ifstream
(3)读写操作---fstream
1.包含头文件fstream | #include |
2.创建 流对象 | ofstream ofs; |
3.指定打开方式 | ofs.open("test.txt", ios::out); |
//4.写内容 | ofs << "姓名:刘家森" << endl; |
//5.关闭文件 | ofs.close(); |
注意事项:(1)文件打开方式可以配合使用,利用 | 操作符。 例如 ios::binary | ios::out 二进制方式写文件
(2)如果写的文件位置没有指定,则默认和cpp文件在一起
1.包含头文件fstream | #include |
2.创建 流对象 | ifstream ifs; |
3.打开文件并判断打开是否成功 | ifs.open("文件路径", 打开方式); |
//4.读数据 | 四种方式 |
//5.关闭文件 | ifs.close(); |
读取方式一:
char buf[1024] = { 0 };
while (ifs >> buf)
{
cout << buf << endl;
}
读取方式二:
char buf[1024] = { 0 };
while (ifs.getline(buf, sizeof(buf)))
{
cout << buf << endl;
}
读取方式三:利用getline函数,把 ifs 的数据读到buf
string buf;
while (getline(ifs, buf))
{
cout << buf << endl;
}
读取方式四:不推荐。利用get函数每次读取单个字符,一直读到最后一个字符
char c;
while ((c = ifs.get()) != EOF)//如果没读到文件尾,EOF=end of line
{
cout << c;
}
#include
using namespace std;
#include
#include
void test1()
{
//1.包含头文件
//2.创建流对象
ifstream ifs;
//打开文件,并且判断是否成功
ifs.open("test.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//4.读数据
///1
//char buf[1024] = { 0 };
//while (ifs >> buf)
//{
// cout << buf << endl;
//}
///2
//char buf[1024] = { 0 };
//while (ifs.getline(buf, sizeof(buf)))
//{
// cout << buf << endl;
//}
///3
//string buf;
//while (getline(ifs, buf))
//{
// cout << buf << endl;
//}
///4 不推荐
//char c;
//while ((c = ifs.get()) != EOF)//如果没读到文件尾,EOF=end of line
//{
// cout << c;
//}
//5.关闭文件
ifs.close();
}
int main()
{
test1();
system("pause");
return 0;
}
二进制文件要加上:ios::binary
#include
using namespace std;
#include
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test1()
{
//1.包含头文件
//2.创建流对象
ofstream ofs("Person.txt", ios::binary | ios::out);// 2,3步骤和为一起
//3.打开文件
//ofs.open("Person.txt", ios::binary | ios::out);
//4.写文件
Person p = { "刘家森",22 };
ofs.write((const char*)&p, sizeof(p));//取地址后强转为 const char *
//5.关闭文件
ofs.close();
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test1()
{
//1.头文件
//2.创建流对象
ifstream ifs;
//3.打开文件,并判断打开成功是否
ifs.open("Person.txt", ios::binary | ios::in);
if (!ifs.is_open())
{
cout << "打开失败"<
针对 c++ 泛型编程 和 STL 技术
模板是一种模板框架。它不可以直接使用,不是万能的。类似证件照的背景图作为模板时,我们只需要把头P上去就行,但是如果给狗拍证件照,就不能用这个模板
语法:template
作用:类型参数化
方式:(1)自动类型推导:MySwap(a,b);直接往函数写入参数
(2)显示指定类型:MySwap
(c, d);写出 T 是什么
#include
using namespace std;
//函数模板
template//声明一个模板,告诉编译器后面代码中紧跟着的T不用报错,T是一个通用数据
void MySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test1()
{
int a = 10;
int b = 20;
//两种方式使用函数模板
//1.自动类型推导
//MySwap(a,b);
cout << a << " " << b<(c, d);
cout << c << " " << d << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:
1.自动类型推导,必须推导出一致的数据类型才可以使用
2.模板必须要确定出 T 的数据类型才可以使用
#include
using namespace std;
template//typename 可以替换为 class
void MySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
//1.自动类型推导,必须推导出一致的数据类型才可以使用
void test1()
{
int a = 10;
int b = 20;
char c = 'c';
MySwap(a, b);//正确
//MySwap(a, c);//错误,数据类型不一致
cout << a << " " << b << endl;
}
//2.模板必须要确定出 T 的数据类型才可以使用
template
void func()
{
cout << "func的调用" << endl;
}
void test2()
{
//func();//错误,没用指定 T 的数据类型
func();//正确
}
int main()
{
test2();
system("pause");
return 0;
}
区别:(1)普通函数调用时可以发送隐式类转换
(2)函数模板-用自动类型推导时,不可以发生隐式类型转换
(3)函数模板-用显示指定类型时,可以发生隐式类型转换
#include
using namespace std;
//普通函数
int MyAdd1(int a, int b)
{
return a + b;
}
void test1()
{
int a = 10;
int b = 20;
char c = 'a';// a 的 ASCALL是97
cout << MyAdd1(a, b) << endl;
cout << MyAdd1(a, c) << endl;//字符型隐式转为了整型
}
//函数模板自动类型推导
template
T MyAdd2(T a, T b)
{
return a + b;
}
void test2()
{
cout << MyAdd2(1, 2) << endl;//正常输出两个 整型
//cout << MyAdd2(1, 'a') << endl;函数不清楚里面什么转什么
}
//函数模板显示指定类型
template
T MyAdd3(T a, T b)
{
return a + b;
}
void test3()
{
cout << MyAdd3(1, 2) << endl;//正常输出两个 整型
cout << MyAdd3(1, 'a') << endl;//发生了隐式转换
}
int main()
{
test1();
test2();
test3();
system("pause");
return 0;
}
规则:(1)如果函数模板和普通函数都可以调用,则优先调用普通函数
(2)可以通过空模板参数列表 强调调用 函数模板
(3)函数模板可以发生函数重载
(4)如果函数模板可以产生更好的匹配,优先调用函数模板
#include
using namespace std;
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 test1()
{
int a = 10;
int b = 20;
//MyPrint(a, b);
//通过空模板参数列表,强制调用函数模板
//MyPrint<>(a, b);
//函数重载
//MyPrint(a, b, 1);
//如果函数模板产生更好的匹配,优先掉函数模板,这里普通函数是 int,模板是 T,所以优先选择 T
char c1 = 'a';
char c2 = 'b';
MyPrint(c1, c2);
}
int main()
{
test1();
system("pause");
return 0;
}
对于一些数据无法实现,例如两个 类 进行比较
作用:(1)利用具体化的模板,实现自定义数据使用
(2)学习模板是为了在 STL 中使用系统模板
#include
using namespace std;
class Person
{
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
template
bool MyCompare(T& a, T& b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
//利用具体化Person的版本来实现代码,优先调用
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 test1()
{
int a = 10;
int b = 10;
bool ret = MyCompare(a, b);
if (ret)
{
cout << "a和b相等" << endl;
}
else
{
cout << "a和b不相等" << endl;
}
}
void test2()
{
Person p1("tom", 10);
Person p2("tom", 10);
bool ret = MyCompare(p1,p2);
if (ret)
{
cout << "p1和p2相等" << endl;
}
else
{
cout << "p1和p2不相等" << endl;
}
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
建立一个通用类,类中的成员,数据类型可以不确定,用一个 虚拟的类型 来代表
语法:template
template
class Person
{
public:
Person(NameTyep name, AgeType age)
{
m_Name = name;
m_Age = age;
}
void showPerson()
{
cout << m_Name << ' ' << m_Age << endl;
}
NameTyep m_Name;
AgeType m_Age;
};
#include
using namespace std;
template
class Person
{
public:
Person(NameTyep name, AgeType age)
{
m_Name = name;
m_Age = age;
}
void showPerson()
{
cout << m_Name << ' ' << m_Age << endl;
}
NameTyep m_Name;
AgeType m_Age;
};
void test1()
{
Person p1("张三", 22);
p1.showPerson();
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:(1)类模板没有自动推导类型,只能使用显示指定类型
(2)类模板在模板参数列表中可以有默认参数
#include
using namespace std;
template //这里赋值了 int
class Person
{
public:
Person(NameType name, AgeType age)
{
m_Name = name;
m_Age = age;
}
void ShowPerson()
{
cout << m_Name<< ' ' <p("孙悟空", 100);//正确,只能用显示指令
p.ShowPerson();
}
//2.类模板在模板参数列表中可以有默认参数
void test2()
{
Personp("猪八戒", 99);//因为默认出写了 int,这里可以写也可以不写
p.ShowPerson();
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
注意事项:(1)普通类中的成员函数一开始就可以创建
(2)类模板中的成员函数在调用时才创建,在类中不会直接一开始就创建
#include
using namespace std;
class Person1
{
public:
void ShowPerson1()
{
cout << "p1 show" << endl;
}
};
class Person2
{
public:
void ShowPerson2()
{
cout << "p2 show" << endl;
}
};
template
class MyClass
{
public:
T obj;
//类模板中的成员函数
void func1()
{
obj.ShowPerson1();
}
void func2()
{
obj.ShowPerson2();
}
};
void test1()
{
MyClassm;
m.func1();
//m.func2();错误,因为此时的 m 是 Person1 类型,不能访问 Person2 类型
}
int main()
{
test1();
system("pause");
return 0;
}
传入方式:(1)指定传入类型(最常见)
(2)参数模板化
(3)整个类模板化
#include
#include
using namespace std;
//类模板对象做函数参数
template
class Person
{
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
void ShowPerson()
{
cout << this->m_Name<<" " << this->m_Age << endl;
}
T1 m_Name;
T2 m_Age;
};
//1.指定传入类型
void PrintPerson1(Person&p)
{
p.ShowPerson();
}
void test1()
{
Personp("孙悟空", 99);
PrintPerson1(p);
}
//2.参数模板化
template
void PrintPerson2(Person& p)
{
p.ShowPerson();
cout << "T1类型:" << typeid(T1).name() << endl;//查看 T1 类型
cout << "T2类型:" << typeid(T2).name() << endl;//查看 T2 类型
}
void test2()
{
Personp("猪八戒", 100);
PrintPerson2(p);
}
//3.整个类模板化
template
void PrintPerson3(T &p)
{
p.ShowPerson();
cout << "T1类型:" << typeid(T).name() << endl;//查看 T1 类型
}
void test3()
{
Personp("唐僧", 10);
PrintPerson3(p);
}
int main()
{
test1();
test2();
test3();
system("pause");
return 0;
}
注意事项:(1)当子类继承的父类是一个类模板时,子类在声明时要给出父类中 T 的类型
(2)如果不指定类型,编译器报错
(3)如果要灵活指出父类中 T 的类型,子类也需要变为类模板
#include
using namespace std;
template
class Base
{
public:
T m;
};
//class Son : public Base//错误 ,必须要知道父类中 T 的类型才能继承给子类
class Son : public Base//正确 ,必须要知道父类中 T 的类型才能继承给子类
{
};
void test1()
{
Son s1;
}
//如果要灵活指定父类中 T 的类型,子类也需要变类模板
template
class Son2 : public Base
{
public:
Son2()
{
cout << "T1类型:" << typeid(T1).name() << endl;
cout << "T2类型:" << typeid(T2).name() << endl;
}
T1 obj;
};
void test2()
{
Son2s2;
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
类模板成员函数在类外实现时,要加上模板的参数列表
#include
using namespace std;
template
class Person
{
public:
Person(T1 name, T2 age);
//{
// m_Name = name;
// m_Age = age;
//}
void showPerson();
//{
// cout << "姓名:" << m_Name << "年龄:"m_Age << endl;
//}
T1 m_Name;
T2 m_Age;
};
//构造函数类外实现
template
Person::Person(T1 name, T2 age)
{
m_Name = name;
m_Age = age;
}
template
void Person::showPerson()
{
cout << "姓名:" << m_Name << "年龄:"<p("ljs", 20);
p.showPerson();
}
int main()
{
test1();
system("pause");
return 0;
}
方式:(1)不包含 .h文件,而是直接包含 .cpp文件
(2)把 h 文件和 cpp 文件写到一起,其后缀为 hpp(常用方法)
注意事项:之所以要这样是因为,类模板的成员属性在函数调用时才确定。如果是只包含 .h 文件,那么在 main 函数中就不会用到 .cpp 文件的 赋值 操作,从而报错
使用:(1)全局函数类内实现:直接在类内声明友元即可(常用方法)
(2)全局函数类外实现:需要提前让编译器知道全局函数的存在(把全局函数放在文件前面,且把类声明放在最前面)
#include
using namespace std;
#include
//提前让编译器知道 person 类的存在
template
class Person;
//通过 全局函数 打印Person信息
template
void PrintPerson2(Personp)
{
cout << "类外实现" << p.m_Name << ' ' << p.m_Age << endl;
}
template
class Person
{
//全局函数 类内实现
friend void PrintPerson(Personp)
{
cout << "类内实现" << p.m_Name << ' ' << p.m_Age << endl;
}
//全局函数 类外实现
//如果全局函数 类外实习按 需要要编译器提前知道这个函数存在
friend void PrintPerson2<>(Personp);//加空模板参数列表
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
void test1()
{
Personp("tom", 20);
PrintPerson(p);
}
void test2()
{
Personp("刘家森", 22);
PrintPerson2(p);
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
(1)长久以来,软件界一直希望建立一种可重复利用的东西。
(2)C++的面向对象和泛型编程思想,目的就是复用性的提升
(3)大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
(4)为了建立数据结构和算法的一套标准,诞生了STL
(1)STL(Standard Template Library,标准模板库)
(2)STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
(3)容器和算法之间通过迭代器进行无缝连接。
(4)STL几乎所有的代码都采用了模板类或者模板函数
STL大体分为六大组件分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
1.容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
2.算法:各种常用的算法,如sort、find、copy.for_each等
3.迭代器:扮演了容器与算法之间的胶合剂。
4.仿函数:行为类似函数,可作为算法的某种策略。
5.适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。6.空间配置器:负责空间的配置与管理。
容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组,链表,树,栈,队列,集合,映射表等这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)算法分为:质变算法和非质变算法。
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
容器:vector
算法:for_each
迭代器:vector
::iterator 注意事项:(1)begin=起始迭代器,指向容器第一个元素
(2)end=结束迭代器,指向容器最后一个元素的下一个位置
(3)push_back 是向容器中插入数据(尾插法)
(4)STL 算法需要包含头文件 #include <algorithm>
遍历方法一:先创建 begin 和 end 再用 while 判断什么时候不一样
通过迭代器访问容器数据
vector::iterator itBegin = v.begin();//begin=起始迭代器,指向容器第一个元素 iterator:迭代器名字
vector::iterator itEnd = v.end();//end=结束迭代器,指向容器最后一个元素的下一个位置
//第一种遍历方式
while (itBegin != itEnd)
{
cout << *itBegin << endl;
itBegin++;
}
遍历方法二:直接创建 begin 和 end 再用for 判断什么时候不一样
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << endl;
}
遍历方法三:调用 for_each 算法,第一个为 begin 第二个为 end 第三个为输出函数
for_each(v.begin(), v.end(), MyPrint);
void MyPrint(int val)
{
cout << val << endl;
}
#include
using namespace std;
#include
#include
void MyPrint(int val)
{
cout << val << endl;
}
void test1()
{
//创建了一个 vector 容器=数组
vector v;
//向容器中插入数据(尾插法)
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
//通过迭代器访问容器数据
//vector::iterator itBegin = v.begin();//begin=起始迭代器,指向容器第一个元素 iterator:迭代器名字
//vector::iterator itEnd = v.end();//end=结束迭代器,指向容器最后一个元素的下一个位置
第一种遍历方式
//while (itBegin != itEnd)
//{
// cout << *itBegin << endl;
// itBegin++;
//}
第二种遍历方式
//for (vector::iterator it = v.begin(); it != v.end(); it++)
//{
// cout << *it << endl;
//}
//第三种遍历方式,利用 STL 提供的遍历算法
for_each(v.begin(), v.end(), MyPrint);
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:看 it 解引用后是什么类型就看 <>里面是什么类型
#include
using namespace std;
#include
class Person
{
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
void test1()
{
vectorv;
Person p1("a", 10);
Person p2("b", 20);
Person p3("c", 30);
Person p4("d", 40);
Person p5("e", 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 << (*it).m_Name<<' '<<(*it).m_Age << endl;//通过解引用访问
cout << it->m_Name << ' ' << it->m_Age << endl;//通过指针访问
}
}
//存放自定义数据类型指针
void test2()
{
vectorv;
Person p1("a", 10);
Person p2("b", 20);
Person p3("c", 30);
Person p4("d", 40);
Person p5("e", 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 << (*it)->m_Name << ' ' << (*it)->m_Age << endl;
}
}
int main()
{
test2();
system("pause");
return 0;
}
容器中的单个部分又是一个容器
#include
using namespace std;
#include
void test1()
{
vector> v;
//创建小容器
vector v1;
vector v2;
vector v3;
vector 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>::iterator it = v.begin(); it != v.end(); it++)
{
//(*it)=小容器vector
for (vector::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
{
cout << *vit << " ";
}
cout << endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
本质:string是C++风格的字符串,而string本质是一个类
区别:(1)string 是一个类,类内部封装了 char*,管理这个字符串,是一个 char*的容器
(2)char*是一个指针
特点:(1)string类内部封装了很多成员方法,包括:find查找,copy拷贝
(2)string管理 char*所分配的内存,不担心取值越界等操作
函数原型:(1) string()默认构造
(2) string (const char * s)字符串初始化
(3) string(const string& str)拷贝构造
(4) string (int n,char c)n个字符c初始化
#include
using namespace std;
//string的构造函数
void test1()
{
string s1;//默认构造
const char* str = "hello world";
string s2(str);//字符串初始化
cout << s2 << endl;
string s3(s2);//拷贝构造
cout << s3 << endl;
string s4(10, 'a');
cout << s4 << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
方式:(1)string& operator=(const char *s)把char* 类型字符串 赋值给当前的字符串
(2)string& operator=(const string &s)把字符串s赋给当前的字符串(拷贝)
(3)string& operator=(char c) 把字符赋值给字符串
(4)string& assign=(const char *s)把字符串s赋给当前的字符串
(5)string& assign=(const char *s,int n)把字符串s的前n个字符赋给当前字符
(6)string& assign=(const string *s)把字符串s赋给当前的字符串(拷贝)
(7)string& assign=(int n,char c) 把n个字符c赋值给当前字符串
#include
using namespace std;
void test1()
{
//1.string& operator=(const char *s)把char* 类型字符串 赋值给当前的字符串
string str1;
str1 = "hello world";
cout << str1 << endl;
//2.string& operator=(const string &s)把字符串s赋给当前的字符串
string str2;
str2 = str1;
cout << str2 << endl;
//3.string& operator=(char c) 把字符赋值给字符串
string str3;
str3 = 'a';
cout << str3<
方式:(1)string& operator+=(const char * str) 重载+=操作符 字符串赋值
(2)string& operator+=(const char c) 重载+=操作符 单字符赋值
(3)string& operator+=(const string& str) 重载+=操作符 字符串变量(拷贝)
(4)string& append(const char *s) 把字符串s连接到当前字符串末尾
(5)string& append(const char *s,int n) 字符串s的前n个字符连接到当前字符串末尾
(6)string& append(const string &s) 把s字符串变量赋值给当前字符串
(7)string& append(const string &s,int pos,int n) 字符串s中从pos开始的n给字符连接到字符串结尾
注意事项:(1)第 7 种方法第一位从 0 开始计数
(2)1汉字=2字符
#include
using namespace std;
void test1()
{
//1.string& operator+=(const char * str) 重载+=操作符 字符串
string str1 = "我";
str1 += "爱玩游戏";
cout << str1 << endl;
//2.string& operator+=(const char c) 重载+=操作符 单字符
str1 += ':';
cout << str1 << endl;
//3.string& operator+=(const string& str) 重载+=操作符 字符串变量
string str2 = "LOL";
str1 += str2;
cout << str1 << endl;
//4.string& append(const char *s) 把字符串s连接到当前字符串末尾
string str4="我爱";
str4.append("罗灿灿");
cout << str4 << endl;
//5.string& append(const char *s,int n) 把字符串s的前n个字符连接到当前字符串末尾
string str5 = "我爱";
str5.append("lccc", 2);//注意事项:1中文=2字符
cout << str5 << endl;
//6.string& append(const string &s) 把s字符串变量赋值给当前字符串
string str6;
str6.append(str5);
cout << str6 << endl;
//7.string& append(const string &s,int pos,int n) 字符串s中从pos开始的n给字符连接到字符串结尾
string str7 = "wo ai ni";
string str8="wo ";
str8.append(str7,3, 5);//注意事项:从 0 开始
cout << str8 << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
查找方法:(1)find:找第一次出现的字符,且返回其下标(未找到返回-1)
(2)rfind:找最后一次出现的字符,且返回其下标(未找到返回-1)
替换方法:str1.replace(1, 3, "1111") 把str1的 第一个元素开始的三个元素换位 1111
#include
using namespace std;
//1.查找
void test1()
{
string str1 = "abcdefgde";
int pos =str1.find("de");//找到返回第一个字符下标,没找到返回-1
if (pos == -1)
{
cout << "没找到字符串" << endl;
}
else
{
cout <<"找到字符串" <
比价方式:字符串比较是按字符串的ASCLL码进行比较
= 返回 0
> 返回 1
< 返回 -1
函数原型:if (str1.compare(str2) == 0) 判断str2和str1是否一样
#include
using namespace std;
void test1()
{
string str1 = "aello";
string str2 = "xello";
if (str1.compare(str2) == 0)
{
cout << "两者相同" << endl;
}
else if (str1.compare(str2) > 0)
{
cout << "str1大" << endl;
}
else
{
cout << "str2大" << endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
方法:(1)char& operator[](int n)通过[]方式读取
(2) char& at(int n)通过at方式获取
#include
using namespace std;
void test1()
{
string str1 = "hello world";
cout << str1 << endl;
//1.通过【】访问单个字符
for (int i = 0; i < str1.size(); i++)
{
cout << str1[i] << ' ';
}
cout << endl;
//2.通过at方式访问单个字符
for (int i = 0; i < str1.size(); i++)
{
cout << str1.at(i) << ' ';
}
cout << endl;
//修改单个字符
str1[0] = 'x';
cout << str1 << endl;
//
str1.at(1) = 'x';
cout << str1 << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:插入和删除起始下标为 0 .也就是说如果从 0 开始是在字符串最左边
#include
using namespace std;
void test1()
{
string str1 = "hello";
//插入
str1.insert(1, "111");//从 第一位 开始插入(没有第0位)
cout << str1 << endl;
//删除
str1.erase(1, 3);
cout << str1 << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
作用:从字符串中获取想要的子串
函数:string substr(int pos = 0, int n = npos)const 返回由pos 开始的n个字符组成的字符串
#include
using namespace std;
void test1()
{
string str = "abcdef";
string str1 = str.substr(0, 3);
cout << str1 << endl;
}
//实用操作:从邮件地址获取姓名
void test2()
{
string str1 = "[email protected]";
int pos = str1.find("@");
string usrName = str1.substr(0, pos);
cout << usrName << endl;
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
vector特点:(1)称为单端数组,也就是尾部是开区间
(2)vector可以进行动态扩展,动态扩展=当数组变大时不是在原内存空间末尾增大,而是把整个新数据开辟一个新的内存空间,然后释放原空间。
#include
using namespace std;
#include
void PrintVector(vector &v)
{
for (vector::iterator it= v.begin(); it != v.end(); it++)
{
cout <<*it<< ' ';
}
cout << endl;
}
void test1()
{
//默认构造
vector v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
PrintVector(v1);
//通过区间构造
vectorv2(v1.begin(), v1.end());
PrintVector(v2);
//n个elem方式构造
vectorv3(10, 100);//将 10 个 100 传入
PrintVector(v3);
//拷贝构造
vectorv4(v3);
PrintVector(v4);
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintVector(vector &v)
{
for (vector::iterator it= v.begin(); it != v.end(); it++)
{
cout <<*it<< ' ';
}
cout << endl;
}
void test1()
{
vectorv1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
PrintVector(v1);
//赋值 =
vectorv2;
v2 = v1;
PrintVector(v2);
//赋值 assign 区间赋值
vectorv3;
v3.assign(v1.begin(), v1.end());
PrintVector(v3);
//赋值 aasign n个elem方式
vectorv5;
v5.assign(5,1);
PrintVector(v5);
}
int main()
{
test1();
system("pause");
return 0;
}
empty:判断数组内容是否为空 1:空
#include
using namespace std;
#include
void PrintVector(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
vectorv1;
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);
PrintVector(v1);//扩大后的值为默认值 0
v1.resize(20, 1);//扩大后的值为 1
PrintVector(v1);
v1.resize(5);
PrintVector(v1);//重新指定之后内存变小了,则去掉多余部分
}
int main()
{
test1();
system("pause");
return 0;
}
插入和删除的位置是:迭代器位置(v.begin())
#include
using namespace std;
#include
void PrintVector(vector&v)
{
for (vector::iterator it = v.begin(); it!= v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
vectorv1;
//尾插法
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
PrintVector(v1);
//尾删法
v1.pop_back();
PrintVector(v1);
//迭代器头部插入 单个
v1.insert(v1.begin(), 10);//第一个参数是 迭代器
PrintVector(v1);
//迭代器头部插入 多个
v1.insert(v1.begin(), 2, 100);//从开始位置 插入 2 个 100;
PrintVector(v1);
//删除 单个
v1.erase(v1.begin());//删除头部第一个
PrintVector(v1);
//删除 多个
v1.erase(v1.begin(),v1.end());//删除从头到尾
PrintVector(v1);
//清空
v1.clear();
PrintVector(v1);
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void test1()
{
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;
//利用 at访问
for (int i = 0; i < v1.size(); i++)
{
cout << v1.at(i) << ' ';
}
cout << endl;
//返回第一个元素
cout << "第一个元素:" << v1.front() << endl;
cout << "最后个元素:" << v1.back() << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
实现两个容器内元素互换。且能够收缩内存空间
函数原型:swap(vec) 将vec与本身元素互换
注意事项:vector
(v1)是创建匿名对象x,x根据v1大小创建合适对象。再利用swap(v1),也就是x和v1分别指向不同地方,而匿名对象x程序会自动释放
#include
using namespace std;
#include
void PrintVector(vector&v)
{
for (vector::iterator it = v.begin(); it!= v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
vectorv1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
cout << "交换前" << endl;
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 test2()
{
vectorv1;
for (int i = 0; i < 100000; i++)
{
v1.push_back(i);
}
cout << v1.capacity() << endl;
cout << v1.size() << endl;
v1.resize(3);//重新指定大小
cout << v1.capacity() << endl;
cout << v1.size() << endl;
//利用swap收缩
vector(v1).swap(v1);//vector(v1)是创建匿名对象x,x根据v1大小创建合适对象。swap(v1)
cout << v1.capacity() << endl;
cout << v1.size() << endl;
}
int main()
{
test2();
system("pause");
return 0;
}
作用:减少vector在动态扩展容量时的扩展次数,在一开始就明确需要多大内存,减少扩展次数
函数原型:reserve(int len) 容器预留len个元素长度,且这些元素没有初始化
#include
using namespace std;
#include
void test1()
{
vectorv1;
//利用reserve预留10000空间,内存空间一次就分配够
v1.reserve(100000);
int num=0;
int* p = NULL;
for (int i = 0; i < 100000; i++)
{
v1.push_back(i);
if (p != &v1[0])//当指针不知向数组首地址时,就重新指向一次,实现计数内存空间改变了几次
{
p = &v1[0];
num++;
}
}
cout << num << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
作用:双端数组,可以对头和尾进行操作
deque和vector区别:(1)vector对于头部插入删除效率低,数据量越大,效率越低
(2)deque插入删除比vector快
(3)vector访问元素比deque快
工作原理:这是一个双端数组,在前和后插入删除时,如果预留空间满了,就会把数据存储到一个缓冲区,再通过一个中控器来存放数据的地址,因此该数组的值可能是非连续存放的
#include
using namespace std;
#include
void PrintDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)//加入const防止修改数据,这里是只读
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
dequed1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
PrintDeque(d1);
//区间赋值
dequed2(d1.begin(), d1.end());
PrintDeque(d2);
//n个值
dequed3(10, 100);
PrintDeque(d3);
//拷贝构造
dequed4(d3);
PrintDeque(d4);
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)//加入const防止修改数据,这里是只读
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
dequed1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
PrintDeque(d1);
//= 赋值
dequed2;
d2 = d1;
PrintDeque(d2);
//assign区间
dequed3;
d3.assign(d1.begin(), d1.end());
PrintDeque(d3);
//assign n个值
dequed4;
d4.assign(10, 100);
PrintDeque(d4);
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:deque没有容量
#include
using namespace std;
#include
void PrintDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)//加入const防止修改数据,这里是只读
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
dequed1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
PrintDeque(d1);
if (d1.empty())
{
cout << "容器为空" << endl;
}
else
{
cout << "容器不为空" << endl;
cout << d1.size() << endl;
}
//重新指定大小 放大 默认填充
d1.resize(15);
PrintDeque(d1);
//重新指定大小 放大 填充 1
d1.resize(17, 1);
PrintDeque(d1);
//重新指定大小 缩小 截取数据
d1.resize(10);
PrintDeque(d1);
}
int main()
{
test1();
system("pause");
return 0;
}
针对迭代器插入
#include
using namespace std;
#include
void PrintDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)//加入const防止修改数据,这里是只读
{
cout << *it << ' ';
}
cout << endl;
}
//两端操作
void test1()
{
dequed1;
//尾插法
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 test2()
{
dequed1;
d1.push_back(10);
d1.push_back(20);
d1.push_front(100);
d1.push_front(200);
PrintDeque(d1);
//在迭代器 插入 单值
d1.insert(d1.begin(), 1000);
PrintDeque(d1);
//在迭代器 插入 多值
d1.insert(d1.begin(),2, 10000);
PrintDeque(d1);
//在迭代器区间位置 插入 多值
dequed2;
d2.push_back(1);
d2.push_back(2);
d2.push_back(3);
d1.insert(d1.begin(),d2.begin(),d2.end() );
PrintDeque(d1);
}
//删除
void test3()
{
dequed1;
d1.push_back(10);
d1.push_back(20);
d1.push_front(100);
d1.push_front(200);
PrintDeque(d1);
//删除 第一个
d1.erase(d1.begin());
PrintDeque(d1);
//删除第二个
deque::iterator it = d1.begin();
it++;
d1.erase(it);
PrintDeque(d1);
//按区间删除
d1.erase(d1.begin(), d1.end());
PrintDeque(d1);
//清空
d1.clear();
PrintDeque(d1);
}
int main()
{
//test1();
//test2();
test3();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)//加入const防止修改数据,这里是只读
{
cout << *it << ' ';
}
cout << endl;
}
//两端操作
void test1()
{
dequed1;
//尾插法
d1.push_back(10);
d1.push_back(20);
d1.push_back(30);
//头插法
d1.push_front(100);
d1.push_front(200);
d1.push_front(300);
//通过 【】访问数据
for (int i = 0; i < d1.size(); i++)
{
cout << d1[i] << ' ';
}
cout << endl;
//通过 at访问
for (int i = 0; i < d1.size(); i++)
{
cout << d1.at(i) << ' ';
}
cout << endl;
//访问头,尾
cout << "第一个元素" << d1.front() << endl;
cout << "最后个元素" << d1.back() << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
void PrintDeque(const deque& d)
{
for (deque::const_iterator it = d.begin(); it != d.end(); it++)//加入const防止修改数据,这里是只读
{
cout << *it << ' ';
}
cout << endl;
}
//两端操作
void test1()
{
dequed1;
//尾插法
d1.push_back(10);
d1.push_back(20);
d1.push_back(30);
//头插法
d1.push_front(100);
d1.push_front(200);
d1.push_front(300);
PrintDeque(d1);
//sort排序规则,从小到大。对于支持随机访问迭代器的容器,也可以sort排序
sort(d1.begin(), d1.end());
PrintDeque(d1);
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:(1)先进后出
(2)不能遍历栈,只能访问栈顶
#include
using namespace std;
#include
void test1()
{
stacks;
//入栈
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;
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:(1)queue=队列
(2)先进先出
(3)只有对队头和队尾可以访问,不能遍历
#include
using namespace std;
#include
class Person
{
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
void test1()
{
//创建队列
queueq;
//准备数据
Person p1("唐僧", 40);
Person p2("孙悟空", 10);
Person p3("猪八戒", 20);
Person p4("沙参", 30);
//入队
q.push(p1);
q.push(p2);
q.push(p3);
q.push(p4);
cout << q.size();
//判断只要队列不为空,查看队头,队尾,出队
while (!q.empty())
{
//查看队头
cout << q.front().m_Name << ' ' << q.front().m_Age << endl;
//查看队尾
cout << q.back().m_Name << ' ' << q.back().m_Age << endl;
//出队
q.pop();
}
cout << q.size();
}
int main()
{
test1();
system("pause");
return 0;
}
list容器=链表。链表由一系列结点构成。
结点:一个是数据域,一个是指针域
注意事项:(1)STL的链表是一个双向循环链表
(2)执行插入和删除不会造成原来迭代器失效
优点:(1)采用动态内存分配,不会造成浪费
(2)执行插入和删除很方便,修改指针就行
缺点:(1)内存消耗大
#include
using namespace std;
#include
void PrintList(const list& l)
{
for (list::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
//默认构造
listl;
//添加数据
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
PrintList(l);
//区间构造
listl2(l.begin(), l.end());
PrintList(l2);
//多值构造
listl3(10,100);
PrintList(l3);
//拷贝构造
listl4(l3);
PrintList(l4);
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintList(const list& l)
{
for (list::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
//默认构造
listl1;
//添加数据
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
PrintList(l1);
//赋值 =
listl2;
l2 = l1;
PrintList(l2);
//赋值 assign区间
listl3;
l3.assign(l2.begin(), l2.end());
PrintList(l3);
//赋值 assign多值
listl4;
l4.assign(10,100);
PrintList(l4);
//交换
listl5;
l5.assign(10, 10);
PrintList(l5);
l4.swap(l5);
PrintList(l4);
PrintList(l5);
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintList(const list& l)
{
for (list::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
//默认构造
listl1;
//添加数据
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
PrintList(l1);
//判断容器是否为空
if (l1.empty())
{
cout << "容器为空" << endl;
}
else
{
cout << "容器不为空" << endl;
cout << "l1的元素个数:" << l1.size() << endl;
}
//重新指定大小 默认补值
l1.resize(10);
PrintList(l1);
//重新指定大小 自己缺点
l1.resize(12,100);
PrintList(l1);
//重新指定大小 缩小
l1.resize(3);
PrintList(l1);
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintList(const list& l)
{
for (list::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
//默认构造
listl1;
//添加数据 //尾插法
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
//头插法
l1.push_front(10);
l1.push_front(20);
l1.push_front(30);
PrintList(l1);
//尾删法
l1.pop_back();
//头删法
l1.pop_front();
PrintList(l1);
//insert插入
l1.insert(l1.begin(), 100);
PrintList(l1);
//迭代器插
list::iterator it = l1.begin();
l1.insert(it++, 200);
PrintList(l1);
//迭代器删除
it = l1.begin();
l1.erase(it);
PrintList(l1);
//移除 指定数据
l1.push_back(10000);
l1.push_front(10000);
PrintList(l1);
l1.remove(10000);
PrintList(l1);
//清空
l1.clear();
PrintList(l1);
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:(1)不能用 L1【0】下标访问,不能用L1.at(0)访问.因为list不是连续线性空间存储数据
(2)不能迭代器进行直接 加 1.例如 it = it +1.但是可以 it++
#include
using namespace std;
#include
void PrintList(const list& l)
{
for (list::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
//默认构造
listl1;
//添加数据 //尾插法
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
//头插法
l1.push_front(10);
l1.push_front(20);
l1.push_front(30);
PrintList(l1);
//不能用 l1【0】下标访问,不能用l1.at(0)访问.因为list不是连续线性空间存储数据
cout << l1.front() << endl;
cout << l1.back() << endl;
//验证迭代器不是随机访问
list::iterator it = l1.begin();
//it = it + 1;错误
//it++//正确
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:(1)sort(l1.begin(), l1.end());错误,因为list容器不支持随机访问,所以不能用标准算法
(2)不支持随机访问迭代器的内部,会提供一些算法:l1.sort();//升序
(3)l1.sort(MyCompare);//降序,里面是一个伪函数
#include
using namespace std;
#include
#include
void PrintList(const list& l)
{
for (list::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
bool MyCompare(int v1, int v2)
{
//降序 就让第一个数 大于 第二个数
return v1 > v2;
}
void test1()
{
//默认构造
listl1;
//添加数据 //尾插法
l1.push_back(1);
l1.push_back(3);
l1.push_back(2);
//头插法
l1.push_front(10);
l1.push_front(200);
l1.push_front(30);
PrintList(l1);
//反转
l1.reverse();
PrintList(l1);
//排序
//sort(l1.begin(), l1.end());错误,因为list容器不支持随机访问,所以不能用标准算法
//不支持随机访问迭代器的内部,会提供一些算法
l1.sort();//升序
PrintList(l1);
l1.sort(MyCompare);//降序
PrintList(l1);
}
int main()
{
test1();
system("pause");
return 0;
}
作用:所有元素会在插入时自动排序(从小到大)
本质:set和multiset属于 关联式容器 ,底层是二叉树
set容器特点:(1)输入无序,但输出有序(从小到大)
(2)set容器中的重复值会被抹去(multiset允许重复值)
#include
using namespace std;
#include
void PrintSet(set&s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
sets1;
//插入数据,只有insert
//这里插入的是无序排序。但输出是有序
s1.insert(10);
s1.insert(20);
s1.insert(70);
s1.insert(40);
s1.insert(20);
//遍历容器
//set容器不允许插入重复值
PrintSet(s1);
sets2(s1);
PrintSet(s2);
//赋值
sets3;
s3 = s2;
PrintSet(s3);
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:set不支持resize()重新分配大小
#include
using namespace std;
#include
void PrintSet(set& s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
sets1;
//插入数据,只有insert
//这里插入的是无序排序。但输出是有序
s1.insert(10);
s1.insert(20);
s1.insert(70);
s1.insert(40);
s1.insert(20);
//遍历容器
//set容器不允许插入重复值
PrintSet(s1);
//判断容器是否为空
if (s1.empty())
{
cout << "容器为空" << endl;
}
else
{
cout << "容器不为空" << endl;
cout << s1.size() << endl;
}
//交换
sets2;
s2.insert(1);
s2.insert(2);
s2.insert(7);
s2.insert(4);
s2.insert(2);
PrintSet(s1);
PrintSet(s2);
s2.swap(s1);
PrintSet(s1);
PrintSet(s2);
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintSet(set& s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
sets1;
//插入数据,只有insert
//这里插入的是无序排序。但输出是有序
s1.insert(10);
s1.insert(20);
s1.insert(70);
s1.insert(40);
s1.insert(20);
s1.insert(80);
s1.insert(180);
//遍历容器
//set容器不允许插入重复值
PrintSet(s1);
//删除 迭代器
s1.erase(s1.begin());
PrintSet(s1);
//删除 迭代器任意位置
set::iterator it = s1.begin();
s1.erase(++it);
PrintSet(s1);
//删除具体数
s1.erase(80);
PrintSet(s1);
//清空
s1.erase(s1.begin(), s1.end());
PrintSet(s1);
//清空
s1.clear();
PrintSet(s1);
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:(1)find函数查找时,查到了返回该元素迭代器。没有查到返回set.end()
(2)统计只能返回 1 或者 0
#include
using namespace std;
#include
void PrintSet(set& s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
sets1;
//插入数据,只有insert
//这里插入的是无序排序。但输出是有序
s1.insert(10);
s1.insert(20);
s1.insert(70);
s1.insert(40);
s1.insert(20);
s1.insert(80);
s1.insert(180);
//遍历容器
//set容器不允许插入重复值
PrintSet(s1);
//查找
set::iterator it = s1.find(20);
if (it != s1.end())
{
cout << "找到了!"<<' ' << *it << endl;
}
else
{
cout << "没找到" << endl;
}
//统计
int num=s1.count(20);
cout << "20个数:" << num << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:(1)set的队组返回 迭代器+bool
(2)multiset队组返回 迭代器
#include
using namespace std;
#include
void PrintSet(multiset& s)
{
for (multiset::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
sets1;
//插入数据,只有insert
//这里插入的是无序排序。但输出是有序
pair::iterator ,bool> ret =s1.insert(10);
if (ret.second)
{
cout << "第一次插入成功" << endl;
}
else
{
cout << "第一次插入失败" << endl;
}
ret = s1.insert(10);
if (ret.second)
{
cout << "第2次插入成功" << endl;
}
else
{
cout << "第2次插入失败" << endl;
}
multisetms;//允许插入重复
ms.insert(20);
ms.insert(20);
ms.insert(10);
PrintSet(ms);
}
int main()
{
test1();
system("pause");
return 0;
}
队组的输出用:first和second
#include
using namespace std;
#include
void PrintSet(multiset& s)
{
for (multiset::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
void test1()
{
//第一种
pair p1("刘家森", 22);
cout << p1.first << ' ' << p1.second << endl;
//第二种
pairp2 = make_pair("罗灿灿", 21);
cout << p2.first << ' ' << p2.second << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintSet(set&s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
class MyCompare
{
public:
bool operator()(int v1,int v2)const
{
return v1 > v2;
}
};
void test1()
{
sets1;
//插入数据,只有insert
//这里插入的是无序排序。但输出是有序
s1.insert(10);
s1.insert(20);
s1.insert(70);
s1.insert(40);
s1.insert(20);
PrintSet(s1);//默认为升序
//指定排序规则为降序
sets2;
//插入数据,只有insert
//这里插入的是无序排序。但输出是有序
s2.insert(10);
s2.insert(20);
s2.insert(70);
s2.insert(40);
for (set::iterator it = s2.begin(); it != s2.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
void PrintSet(set& s)
{
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
class Person
{
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
class MyCompare
{
public:
bool operator()(const Person &p1, const Person &p2) const
{
return p1.m_Age > p2.m_Age;
}
};
void test1()
{
sets;
Person p1("刘备", 24);
Person p2("关羽", 28);
Person p3("张飞", 25);
Person p4("赵云", 21);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for (set::iterator it = s.begin(); it != s.end(); it++)
{
cout << it->m_Name << ' ' << it->m_Age << endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:map中所有元素都是成对出现,插入数据时要使用对组
#include
using namespace std;
#include
#include
using namespace std;
#include
插入第一种
m.insert(pair(1, 10));
插入第2种
m.insert(make_pair(2, 20));
插入第3种
m.insert(map::value_type(4, 30));
插入第4种 insert 不推荐!!因为会容易误插。但可以利用key访问value
m[3] = 40;
#include
using namespace std;
#include
find:返回的是迭代器
count:map返回1和0 multimap任意返回
#include
using namespace std;
#include
#include
using namespace std;
#include
概念:(1)重载函数调用操作符的类,其对象称为函数对象
(2)函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质:函数对象(仿函数)是一个类,不是一个函数
特点:(1)函数对象(仿函数)在使用时,可以像普通函数那样调用,可以有参数,也可以有返回值
(2)函数对象超出普通函数的概念,可以拥有自己的状态
(3)函数对象可以组为参数进行传递
#include
using namespace std;
//1.函数对象(仿函数)在使用时,可以像普通函数那样调用,可以有参数,也可以有返回值
class MyAdd
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
//2.函数对象超出普通函数的概念,可以拥有自己的状态
class MyPrint
{
public:
MyPrint()
{
count = 0;
}
void operator()(string test)
{
cout << test << endl;
count++;
}
int count;//内部自己状态记录
};
void test1()
{
MyAdd myadd;
cout << myadd(10, 10) << endl;
}
void test2()
{
MyPrint myprint;
myprint("hello world");
myprint("hello world");
cout << myprint.count << endl;
}
void doPrint(MyPrint& mp, string test)
{
mp(test);
}
//3.函数对象可以组为参数进行传递
void test3()
{
MyPrint myPrint;
doPrint(myPrint, "C++");
}
int main()
{
test1();
test2();
test3();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
class GreatFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test1()
{
vectorv;
GreatFive greatfive;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找容器中有没有大于5的数
//GreatFive()是匿名对象
//find_if(v.begin(), v.end(), greatfive);//正确
vector::iterator it = find_if(v.begin(), v.end(), GreatFive());
if (it == v.end())
{
cout << "没找到" << endl;
}
else
{
cout << "找到了:" <<*it<< endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
class GreatFive
{
public:
bool operator()(int v1,int v2)
{
return v1 > v2;
}
};
void test1()
{
vectorv;
GreatFive greatfive;
v.push_back(1);
v.push_back(3);
v.push_back(2);
v.push_back(5);
v.push_back(4);
sort(v.begin(),v.end());
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
//使用函数对象 改变算法,实现降序排列
sort(v.begin(), v.end(), GreatFive());
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
在STL系统自带了一些仿函数,包括:算术,关系,逻辑
注意事项:使用时要加入头文件 <functional>
除 negate运算是一元运算,其它都是二元运算
#include
using namespace std;
#include
void test1()
{
//neagte 一元运算(取反)
negaten;
cout<m;
cout << m(10, 20) << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
仿函数部分写:greater
()
#include
using namespace std;
#include
#include
#include
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};
void test1()
{
vectorv;
v.push_back(1);
v.push_back(5);
v.push_back(3);
v.push_back(2);
v.push_back(4);
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
//降序 自己写仿函数
//sort(v.begin(), v.end(), MyCompare());
//系统自带仿函数 greater
sort(v.begin(), v.end(), greater());
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
注意事项:transform搬运的容器要先设置大小
#include
using namespace std;
#include
#include
#include
void test1()
{
//逻辑非 logical_not
vectorv;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
//利用逻辑非 将上面容器 搬到v2中,并执行取反
vectorv2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not());
for (vector::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
实现单纯的遍历
#include
using namespace std;
#include
#include
//普通函数
void print1(int val)
{
cout << val << ' ';
}
class prin2
{
public:
void operator()(int val)
{
cout << val << ' ';
}
};
void test1()
{
vectorv;
v.push_back(1);
v.push_back(3);
v.push_back(5);
v.push_back(2);
v.push_back(4);
//方法一 普通函数 放函数名字
for_each(v.begin(), v.end(), print1);
cout << endl;
//方法二 仿函数 放匿名对象
for_each(v.begin(), v.end(), prin2());
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
实现在容器数据搬移过程中再处理一下
#include
using namespace std;
#include
#include
class Transform
{
public:
int operator()(int val)
{
return val+100;
}
};
class Print
{
public:
void operator()(int val)
{
cout<v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vectorvTarget;//目标容器
vTarget.resize(v.size());
transform(v.begin(), v.end(), vTarget.begin(), Transform());
for_each(vTarget.begin(), vTarget.end(), Print());
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
find在容器中查找不管找没找到返回的是迭代器
#include
using namespace std;
#include
#include
void test1()
{
//查找 内置数据类型
vectorv;
for(int i=0;i<10;i++)
{
v.push_back(i);
}
//查找有没有 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)
{
m_Name = name;
m_Age = age;
}
//重载 == 才能实现 Person比较
bool operator==(const Person &p1)
{
if (this->m_Name == p1.m_Name && this->m_Age == p1.m_Age)
{
return true;
}
else
{
return false;
}
}
string m_Name;
int m_Age;
};
void test2()
{
//查找 自定义数据类型
vectorv;
//创建数据
Person p1("aa",10);
Person p2("bb", 20);
Person p3("cc", 30);
Person p4("dd", 40);
//放入容器
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
//查找 y有没有 pp这个人在容器中
Person pp("dd", 40);
vector::iterator it = find(v.begin(), v.end(), pp);
if (it == v.end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到了:" << ( * it).m_Name<<' '<m_Age << endl;
}
}
int main()
{
test2();
system("pause");
return 0;
}
find在容器中查找不管找没找到返回的是迭代器,并且仿函数是谓词
#include
using namespace std;
#include
#include
class GreatFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test1()
{
//查找 内置数据类型
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//查找有没有 5
vector::iterator it = find_if(v.begin(), v.end(), GreatFive());
if (it == v.end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到了:" << *it << endl;
}
}
class Person
{
public:
Person(string name, int age)
{
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
class Greta20
{
public:
bool operator()(const Person& p1)
{
return p1.m_Age > 20;
}
};
void test2()
{
//查找 自定义数据类型
vectorv;
//创建数据
Person p1("aa", 10);
Person p2("bb", 20);
Person p3("cc", 30);
Person p4("dd", 40);
//放入容器
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
//查找 y有没有 pp这个人在容器中
Person pp("dd", 40);
vector::iterator it = find_if(v.begin(), v.end(), Greta20());
if (it == v.end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到了:" << (*it).m_Name << ' ' << it->m_Age << endl;
}
}
int main()
{
test2();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
void test1()
{
vectorv;
v.push_back(0);
v.push_back(2);
v.push_back(0);
v.push_back(3);
v.push_back(1);
v.push_back(4);
v.push_back(3);
v.push_back(3);
vector::iterator it = adjacent_find(v.begin(),v.end());
if (it == v.end())
{
cout << "没有找到" << endl;
}
else
{
cout << "找到了:" << (*it) << endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
容器数据必须有序,如果无序,则结果输出不一定正确
#include
using namespace std;
#include
#include
void test1()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
bool ret = binary_search(v.begin(), v.end(), 9);
if (ret )
{
cout << "找到了" << endl;
}
else
{
cout << "没有找到:" << endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
(1)返回一个 int类型数据,代表计数次数
(2)函数重载的是 == ,且返回值类型为 bool
#include
using namespace std;
#include
#include
void test1()
{
//内置数据
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
v.push_back(1);
v.push_back(1);
int ret = count(v.begin(), v.end(), 1);
cout << ret << endl;
}
class Person
{
public:
Person(string name,int age)
{
m_name = name;
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 test2()
{
// 自定义数据类型
vectorv;
Person p1("刘备", 72);
Person p2("关羽", 32);
Person p3("张飞", 42);
Person p4("曹操", 72);
Person p5("赵云", 62);
Person pp("诸葛亮", 72);
//将人员插入容器
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
int num = count(v.begin(), v.end(), pp);
cout << "和诸葛亮年纪一样的有:" << num << endl;
}
int main()
{
test2();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
class Greater5
{
public:
bool operator()(int v)
{
return v > 5;
}
};
void test1()
{
//内置数据
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
v.push_back(20);
v.push_back(1);
int ret = count_if(v.begin(), v.end(), Greater5());
cout << ret << endl;
}
class Person
{
public:
Person(string name, int age)
{
m_name = name;
m_age = age;
}
string m_name;
int m_age;
};
class Greater50
{
public:
bool operator()(const Person &p)
{
if (p.m_age > 52)
{
return true;
}
else
{
return false;
}
}
};
void test2()
{
// 自定义数据类型
vectorv;
Person p1("刘备", 72);
Person p2("关羽", 32);
Person p3("张飞", 42);
Person p4("曹操", 72);
Person p5("赵云", 62);
//将人员插入容器
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(), Greater50());
cout << "大于52的有:" << num << endl;
}
int main()
{
test2();
system("pause");
return 0;
}
sort算法。默认为升序,如果想改为降序,可以自己写仿函数,或者直接调用内建函数greater
()
#include
using namespace std;
#include
#include
#include
void MyPrint(int v)
{
cout << v << ' ';
}
void test1()
{
vectorv;
v.push_back(1);
v.push_back(3);
v.push_back(5);
v.push_back(2);
v.push_back(4);
//利用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()
{
test1();
system("pause");
return 0;
}
//928
#include
using namespace std;
#include
#include
#include
#include
void MyPrint(int v)
{
cout << v << ' ';
}
void test1()
{
vectorv;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
//利用洗牌算法打乱顺序
random_shuffle(v.begin(), v.end());
for_each(v.begin(), v.end(), MyPrint);
cout << endl;
}
int main()
{
srand((unsigned int)time(NULL));
test1();
system("pause");
return 0;
}
//928
给目标容器分配内存空间是两个原容器大小之和
#include
using namespace std;
#include
#include
#include
void MyPrint(int v)
{
cout << v << ' ';
}
void test1()
{
vectorv;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vectorv1;
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
v1.push_back(7);
v1.push_back(8);
//合并
vectorv2;
//目标容器分配内存
v2.resize(v.size()+v1.size());
merge(v.begin(), v.end(), v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), v2.end(), MyPrint);
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
//928
#include
using namespace std;
#include
#include
#include
void MyPrint(int v)
{
cout << v << ' ';
}
void test1()
{
vectorv;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
//翻转
reverse(v.begin(), v.end());
for_each(v.begin(), v.end(), MyPrint);
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
//928
#include
using namespace std;
#include
#include
void MyPrin(int v)
{
cout << v << ' ';
}
void test1()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vectorv2;
v2.resize(v.size());
//复制 v 到 v2
copy(v.begin(), v.end(), v2.begin());
for_each(v2.begin(), v2.end(), MyPrin);
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
//仿函数
class MyPrint
{
public:
void operator()(int v)
{
cout << v << ' ';
}
};
void test1()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
//把 v 中的 9 替换为 90
replace(v.begin(), v.end(), 9, 90);
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include
using namespace std;
#include
#include
//谓词
class Greater5
{
public:
bool operator()(int v)
{
return v >= 5;
}
};
//仿函数
class MyPrint
{
public:
void operator()(int v)
{
cout << v << ' ';
}
};
void test1()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
//把 v 中的 大于5的 都替换为 50
replace_if(v.begin(), v.end(), Greater5(), 50);
for_each(v.begin(), v.end(), MyPrint());
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
交换的两个容器,数据类型要相同
#include
using namespace std;
#include
#include
void MyPrin(int v)
{
cout << v << ' ';
}
void test1()
{
vectorv;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vectorv2;
for (int i = 10; i < 20; i++)
{
v2.push_back(i);
}
for_each(v.begin(), v.end(), MyPrin);
cout << endl;
for_each(v2.begin(), v2.end(), MyPrin);
cout << endl;
//交换 v 和 v2
swap(v, v2);
for_each(v.begin(), v.end(), MyPrin);
cout << endl;
for_each(v2.begin(), v2.end(), MyPrin);
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
(1)value:0为返回的起始值,写0则为sum=sum+0 写10就为 sum=sum+10
(2)该函数返回一个累加值
(3)头文件:numeric
#include
using namespace std;
#include
#include
void test1()
{
vectorv;
for (int i = 0; i <= 100; i++)
{
v.push_back(i);
}
//计算容器和 0为返回的起始值,写0则为sum=sum+0 写10就为 sum=sum+10
int sum=accumulate(v.begin(), v.end(),0);
cout << sum << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
//928
#include
using namespace std;
#include
#include
#include
void MyPrint(int v)
{
cout << v << ' ';
}
void test1()
{
vectorv;
v.resize(10);//默认填充为 0
//重新填充容器 把默认值0 全部填充为 100
fill(v.begin(), v.end(), 100);
for_each(v.begin(), v.end(),MyPrint);
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
//928
(1)求交集的两个集合必须有序
(2)遍历输出交集时,要用函数返回值当停止位的迭代器
#include
using namespace std;
#include
#include
class MyPrint
{
public:
void operator()(int v)
{
cout<< v<<' ';
}
};
void test1()
{
vectorv1;
vectorv2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i + 5);
}
//目标容器需要提前开辟内存 空间取小容器size
vectorvTarget;
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()
{
test1();
system("pause");
return 0;
}
(1)求并集的两个集合必须位有序序列
(2)目标容器开辟空间为两个容器相加
(3)set_union返回的是并集中最后一个元素位置
#include
using namespace std;
#include
#include
class MyPrint
{
public:
void operator()(int v)
{
cout << v << ' ';
}
};
void test1()
{
vectorv1;
vectorv2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i + 5);
}
//目标容器需要提前开辟内存 空间容器和
vectorvTarget;
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()
{
test1();
system("pause");
return 0;
}
(1)求差集的两个集合必须有序序列
(2)目标容器kai'pi空间需要从两个容器取较大值
(3)set_difference返回值是cha差集最后一个元素
#include
using namespace std;
#include
#include
class MyPrint
{
public:
void operator()(int v)
{
cout << v << ' ';
}
};
void test1()
{
vectorv1;
vectorv2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i + 5);
}
//目标容器需要提前开辟内存 空间容器和
vectorvTarget;
vTarget.resize(max(v1.size(),v2.size()));
//求差集 得到差集位置迭代器位置
vector::iterator itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
cout << "v1和v2差集" << endl;//v2和v1差集不一样
for_each(vTarget.begin(), itEnd, MyPrint());
cout << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
目的:对于一串数字进行按顺序大小排序,常用于一维数组排序问题!因为这个算法每次遍历输出一个最大值,所以叫冒泡——一个一个最大泡
核心:排序轮数:元素个数-1
对比次数:元素个数-排序轮数-1
利用temp中间值进行交换
for (int i = 0; i < 9; i++)
{
//进行循环对比 次数为:元素个数-排序轮数-1
for (int j = 0; j < 9 - i - 1; j++)
{
//如果第一个大于第二个,则交换
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
原理:以数组中第一个数为最小值,然后利用一个 for 去比较 该值和数组中其他值。并且如果该值小于其他值,则返回大值的下标。然后在用第二个数进行比较
for (int i = 0; i < m_EmpNum; i++)
{
int minOrMax = i;//声明最小值 或 最大值
for (int j = i + 1; j < this->m_EmpNum; j++)
{
if (select == 1)
{
if (this->m_EmpArray[minOrMax]->m_id> this->m_EmpArray[j]->m_id)
{
minOrMax = j;
}
}
else//降序
{
if (this->m_EmpArray[minOrMax]->m_id < this->m_EmpArray[j]->m_id)
{
minOrMax = j;
}
}
}
//判断一开始认定的 最小值或最大值 是不是计算的最小值或最大值,如果不是,交换数据
if (i != minOrMax)
{
Worker* temp = this->m_EmpArray[i];
this->m_EmpArray[i] = this->m_EmpArray[minOrMax];
this->m_EmpArray[minOrMax] = temp;
}
}