重生之——c++与我不得不说的一些事


看这里!!!本文顺序按照黑马视频所写,不仅对视频内容重点进行了分析,并且包含了黑马视频课程所有代码,代码链接已经贴在了下方(工程文件可直接运行),如果你觉得有帮助的话,可以动动发财小手,帮我点个赞哦!部分文件截图如下:

重生之——c++与我不得不说的一些事_第1张图片

链接: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丶选择排序


1丶指针

2丶内存分区模型

3丶引用

4丶函数

5丶类和对象

6丶继承

7丶多态

8丶文件操作

9丶模板

10丶STL-常用容器

11丶STL-函数对象

12丶STL-常用算法

附录丶算法


1.1 const

1.1.1常量指针

const int * p = &a    const修饰指针

指针的指向可以改

指针的内容不能改

	int a = 10, b = 20;
	const int* p1;//定义
	//*p1 = 10;错误
	p1 = &b;

1.1.2指针常量

int * const p = &a      const修饰常量

指针的指向不能改

指针的内容可以改

	int c = 10, d = 20;
	int* const p2 = &c;//定义
	*p2 = 30;
	//p2=&d 错误

1.1.3修饰指针+常量

const int * const p = &a     const修饰指针和常量

指针的指向不能改

指针的内容不能改

	int e = 10, f = 20;
	const int * const p3= &e;
	//*p3 = 10;错误
	//p3=&f 错误

2.1程序运行前

2.1.1全局区

重生之——c++与我不得不说的一些事_第2张图片

2.1.2代码区

重生之——c++与我不得不说的一些事_第3张图片

 2.2程序运行后

2.2.1栈区

 2.2.2堆区

重生之——c++与我不得不说的一些事_第4张图片

 把一个整型数据10放到堆区,然后把这个10的地址用栈区的指针来保存,并且堆区的这个整型数据10是是否存在取决于程序员。

int * p=new int (10);  把10放入堆区,并把10的地址给了指针放在栈区

2.3new操作符

2.3.1new的语法

new:把数据保存到堆区,并且返回该数据地址

delete:删除堆区数据,释放内存

//单个数据:
int* p = new int(10);     
delete p;
//一个数组:
int* p = new int(10);     
delete[] arr;

3.1引用的基本使用

语法:数据类型 &别名=原名     例如  int &b= a

作用:给数据起另外一个名字,例如现在的b就是a,具有相同地址,并且改变b也会改变a

3.2引用的注意事项

1.必须初始化:例如  int &b是错的

2.引用初始化之后不能改变:例如 b 是 a 的别名,那么 b 不能再是 c 的别名(可以b=c因为这是赋值操作,而不是引用操作)

3.3引用做函数参数

名称:引用传递

作用:交换实参,实现函数的地址传递,简化指针修改实参

void swap3(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

3.4引用做函数返回值

作用:作为函数返回值返回

	int &ref = test2();
	cout << ref << endl;
	test2() = 100;//本质是令a=100
	cout << ref << endl;

注意:不要返回局部变量引用(x64可以,x86不行)

3.5引用的本质

本质:引用的本质在c++内部是一个指针常量

int& ref = a;
//自动转换为 int *const ref = &a 指针常量是指针指向不能变,也就是引用不能变
func(a)
void func(int& ref)
//转为 int *const ref=&a;
{
	ref = 100;//ref是引用,转为 *ref=100
}

3.6常量引用

作用:防止形参修改实参的值

void showValue(const int &val)
{
	//val = 100;//不能修改 val 的值
	cout << val << endl;
}

4.1函数默认参数

作用:给函数形参赋值一个初始值

注意事项:(1)如果有默认值,且有形参,则按形参值处理

                  (2)某个位置有默认形参时,后面参数都必须有形参默认值

                  (3)如果声明有默认参数值,则函数实现不能有默认参数值。声明和实现只能有一个有默认值

 func(10,20)
int func(int a,int b=10,int c=30)
{
	return a + b + c;
}

4.2函数占位参数

语法:返回值类型 函数名 (数据类型){}

作用:意义不大!!!

注意事项:占位参数也可以有默认值:void func(int a,int=10)

func(20,10);
void func(int a,int)
{
	cout << "这是函数调用" << endl;
}

4.3函数重载

4.3.1函数重载概述

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

条件:(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)

4.3.2函数重载注意事项

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;
}

5类和对象

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

人作为对象,其属性有:姓名,身高,体重,其行为有走,吃饭,睡觉

成员:包括类中的属性和行为

        (1)属性:成员属性=成员变量

        (2)行为:成员函数=成员方法

:一个类中可以包含另外一个类

5.1封装

5.1.1封装的语法

意义:(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;

5.1.2封装的权限控制

公共权限 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;
	}
};

5.1.3 struct 和 calss 区别

struct  默认权限为公告,所以类外能访问
calss   默认权限为私有,所以类外不能访问

5.1.4 成员属性设置为私有化

好处:自己控制读写权限

class Person
{
public://在类里面设置一个公告接口,来设置调用私有属性的值
	//写姓名
	void setName(string name)
	{
		m_name = name;
	}
	//读姓名
	string getName()
	{
		return m_name;
	}
private://在类里面设置属性为私有
	string m_name;//可读可写
};

5.2对象的初始化和清理

对于数据的初始化以及销毁之前的清理数据操作

5.2.1构造函数和析构函数语法

构造函数:初始化设置,编译器自动调用

析构函数:对象销毁前系统自动调用,执行清理工作

注意事项:如果自己不写这些函数,编译器会提供这些函数(空实现,空代码)

重生之——c++与我不得不说的一些事_第5张图片

class Person
{
public:
	//1,构造函数
	Person()
	{
		cout << "Person构造函数的调用" << endl;
	}
	//2,析构函数
	~Person()
	{
		cout << "Person析构函数的调用" << endl;
	}
};

5.2.2构造函数的分类及调用

分类

按参数分类:无参(默认方式) 有参

按类型分类:普通 拷贝

调用: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;

5.2.3拷贝构造函数调用

通常有三种情况:(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();

}

5.2.4构造函数调用规则

默认情况:C++会提供  默认构造(空函数),有参构造(空函数),拷贝构造(值传递)

规则:(1)如果用户定义有参,系统不再提供无参(无法调用),但提供拷贝(可调用)

           (2)如果用户定义拷贝,系统不再提供 无参有参(都无法调用)

5.2.5深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝(默认方式)

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

注意事项:(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;
	}

5.2.6初始化列表

语法:构造函数():属性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);
}

5.2.7类对象作为类成员

构造:先构造对象,再构造自身

析构:先释放自身,再释放对象

解释:当一个类作为另外一个类的成员时,先构造该成员再构造该类==先有了人的零件才能有完整的人,先构造对象,再构造自身。。析构函数先退出自身,再退出对象

5.2.8静态成员变量

语法: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;
}

5.2.9静态成员函数

语法: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;
}

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

5.3.1成员变量和成员函数分开存储

非静态成员:属于类对象

静态成员,非静态成员函数,静态成员函数:不属于类对象

注意事项:(1)空对象,则分配一个字节

                  (2)在判断属不属于类对象时,会影响该对象在内存中分配的字节。例如,当 类 里面有 两个 int 和一个 static int 时,该对象占 2*4 个字节

class Person
{
	int m_A;//非静态成员属于类的对象上
	static int m_B;//静态成员不属于类的对象上
	void func() {}//非静态成员函数不属于类的对象上
	static void func2() {}//静态成员函数不属于类的对象上
};

5.3.2this指针

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,所以
    //可以继续调用
}

5.3.3空指针调用成员函数

注意事项:(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();//可以正常访问
}

5.3.4const修饰成员函数

常函数:(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  常对象无法调用 非常函数
}


5.4友元

利用 友元关键字 friend 可以访问类中的私有成员

5.4.1全局函数做友元

作用:为了使全局函数可以访问私有类

语法:在 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);
}

5.4.2类做友元

作用:为了使类可以访问其他私有类

语法:在 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,所以可以访问私有,输出结果

5.4.3成员函数做友元

作用:为了使类可以访问其他私有类

语法:在 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();
}

5.5运算符重载

概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同数据类型

5.5.1加号运算符重载

方法:(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;
}

5.5.2左移运算符重载

方法:全局函数重载

作用:输出任意类型数据,例如 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<

5.5.3递增递减运算符重载

前置递增:返回引用。先++,再返回。

后置递增:返回值。先返回,再++。

注意事项:(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;
}

5.5.4赋值运算符重载

作用:因为系统默认提供浅拷贝,为了防止析构函数报错,所以写入深拷贝。

注意事项:(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;
}

5.5.5关系运算符重载

作用:对于自定义数据类型进行判断 是否相等

#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;
}

5.5.6函数调用运算符重载

仿函数:重载()的函数

匿名函数对象:没有名字+仿函数

注意事项:仿函数类型灵活

#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 <

6继承

重生之——c++与我不得不说的一些事_第6张图片

 下级成员拥有上一级的共性,并且还有自己的特性,这时通过继承,可以减少代码重复度

6.1继承的基本语法

语法:  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;
}

6.2继承方式

继承方式:公共继承,保护继承,私有继承

注意事项:(1)三种继承方式都无法继承父类的私有权限

                   (2)公共继承得到的和之前父类权限一样

                   (3)保护继承得到的全部为保护权限

                   (4)私有继承得到的全部为私有权限

重生之——c++与我不得不说的一些事_第7张图片

#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;
}

6.3继承中的对象模型

注意事项:(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;
}

6.4继承中构造和析构顺序

子类继承父类之后,也会有父类的构造函数和析构函数

注意事项:(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;
}

6.5继承同名成员处理方式

作用:解决子类和父类同名成员问题

注意事项:(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;
}

6.6.继承同名静态成员处理方式

作用:解决子类和父类静态同名成员问题

方式:(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;
}

6.7多继承语法

作用:一个类继承多个类

语法: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;
}

6.8菱形继承

概念:两个派生类共同继承一个基类,并且这两个派生类被一个派生类继承,这种叫零次菱形继承或砖石继承

虚继承:在继承方式之前加 virtual。本质是进行指针调用

注意事项:(1)由于数据继承了两个,但是只需要一个,解决办法加 作用域

                   (2)如果只要一份数据,则可以采用 虚继承

重生之——c++与我不得不说的一些事_第8张图片

#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;
}

7多态

分类:(1)静态多态:函数重载 和 运算符重载属于静态,复用函数名

           (2)动态多态:派生类 和虚函数实现运行时多态

区别:静态多态是函数早绑定,在编译阶段确定。动态多态是函数晚绑定,运行阶段确定

7.1多态的基本使用

作用:能够根据不同类确定调用相同名字的函数

动态多态的条件:(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;
}

7.2多态使用的原理

原理:当父类的函数为虚函数时,此时父类中是一个 虚函数指针 指向该 父类虚函数。此外,对于子类中的同名函数,子类中也是一个虚函数指针,不过指向的是 子类虚函数。因为子类中的虚函数表覆盖了父类的虚函数表。

重生之——c++与我不得不说的一些事_第9张图片7.3纯虚函数和抽象类

纯虚函数:虚函数中无实际调用    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;
}

7.4虚析构和纯虚析构

虚析构:语法: 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;
}

8文件操作

作用:程序运行时的数据都属于临时数据,程序运行结束时会被释放。通过文件可以将数据持久化。

分类:(1)文本文件,以ASCALL码存储

           (2)二进制文件,以二进制形式存储

三大类:(1)写操作---ofstream

              (2)读操作---ifstream

              (3)读写操作---fstream

8.1文本文件

8.1.1写文件

写文件步骤
1.包含头文件fstream #include
2.创建 流对象 ofstream ofs;
3.指定打开方式 ofs.open("test.txt", ios::out);
//4.写内容 ofs << "姓名:刘家森" << endl;
//5.关闭文件     ofs.close();

重生之——c++与我不得不说的一些事_第10张图片

 注意事项:(1)文件打开方式可以配合使用,利用 | 操作符。   例如 ios::binary | ios::out     二进制方式写文件

                   (2)如果写的文件位置没有指定,则默认和cpp文件在一起

8.1.2读文件

读文件步骤
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;
}

8.2二进制文件

二进制文件要加上:ios::binary

8.2.1写文件

重生之——c++与我不得不说的一些事_第11张图片

#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;
}

8.2.2读文件

重生之——c++与我不得不说的一些事_第12张图片

#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 << "打开失败"<

9模板

针对 c++ 泛型编程 和 STL 技术

9.1模板的概念

模板是一种模板框架。它不可以直接使用,不是万能的。类似证件照的背景图作为模板时,我们只需要把头P上去就行,但是如果给狗拍证件照,就不能用这个模板

9.2函数模板

9.2.1函数模板语法

语法: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;
}

9.2.2函数模板注意事项

注意事项:

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;
}

9.2.3普通函数和函数模板区别

区别:(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;
}

9.2.4普通函数和函数模板的调用规则

规则:(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;
}

9.2.5模板的局限性

对于一些数据无法实现,例如两个 类 进行比较

作用:(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;
}

9.3类模板

建立一个通用类,类中的成员,数据类型可以不确定,用一个 虚拟的类型 来代表

9.3.1类模板语法

语法: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;
}

9.3.2类模板于函数模板区别

注意事项:(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;
}

9.3.3类模板中成员函数创建时机

注意事项:(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;
}

9.3.4类模板对象做函数参数

传入方式:(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;
}

9.3.5类模板与继承

注意事项:(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;
}

9.3.6类模板成员函数类外实现

类模板成员函数在类外实现时,要加上模板的参数列表

#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;
}

9.3.7类模板分文件编写

方式:(1)不包含 .h文件,而是直接包含 .cpp文件

           (2)把 h 文件和 cpp 文件写到一起,其后缀为 hpp(常用方法)

注意事项:之所以要这样是因为,类模板的成员属性在函数调用时才确定。如果是只包含 .h 文件,那么在 main 函数中就不会用到 .cpp 文件的 赋值 操作,从而报错

9.3.8类模板与友元

使用:(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;
}

10STL-常用容器

10.1STL初始

10.1.1STL的诞生

(1)长久以来,软件界一直希望建立一种可重复利用的东西。

(2)C++的面向对象泛型编程思想,目的就是复用性的提升

(3)大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作

(4)为了建立数据结构和算法的一套标准,诞生了STL

10.1.2STL基本概念

(1)STL(Standard Template Library,标准模板库

(2)STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)

(3)容器算法之间通过迭代器进行无缝连接。

(4)STL几乎所有的代码都采用了模板类或者模板函数

10.1.3STL六大组件

STL大体分为六大组件分别是:容器算法迭代器仿函数适配器(配接器)空间配置器


1.容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
2.算法:各种常用的算法,如sort、find、copy.for_each等
3.迭代器:扮演了容器与算法之间的胶合剂。
4.仿函数:行为类似函数,可作为算法的某种策略。
5.适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。6.空间配置器:负责空间的配置与管理。

10.2STL中容器,算法,迭代器

10.2.1STL中容器,算法,迭代器概念

容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组,链表,树,栈,队列,集合,映射表等

这些容器分为序列式容器关联式容器两种:


序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。

关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)

算法分为:质变算法非质变算法
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
 

迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

10.2.2vector存放内置数据类型

容器: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;
}

10.2.3vector存放自定义数据类型

注意事项:看 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;
}

10.2.4vector容器嵌套容器

容器中的单个部分又是一个容器

#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;
}

10.3string容器

10.3.1string容器基本概念

本质:string是C++风格的字符串,而string本质是一个类

区别:(1)string 是一个类,类内部封装了 char*,管理这个字符串,是一个 char*的容器

           (2)char*是一个指针

特点:(1)string类内部封装了很多成员方法,包括:find查找,copy拷贝

           (2)string管理 char*所分配的内存,不担心取值越界等操作

10.3.2string构造函数

函数原型:(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;
}

10.3.3string赋值操作

方式:(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<

10.3.4string字符串拼接

方式:(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;
}

10.3.5string查找和替换

重生之——c++与我不得不说的一些事_第13张图片

 查找方法:(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 <<"找到字符串" <

10.3.6string字符串比较

比价方式:字符串比较是按字符串的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;
}

10.3.7string字符读写

方法:(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;
}

10.3.8string插入和删除

重生之——c++与我不得不说的一些事_第14张图片

注意事项:插入和删除起始下标为 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;
}

10.3.9string子串

作用:从字符串中获取想要的子串

函数: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;
}

10.4vector容器

10.4.1vector基本概念

重生之——c++与我不得不说的一些事_第15张图片

vector特点:(1)称为单端数组,也就是尾部是开区间

                     (2)vector可以进行动态扩展,动态扩展=当数组变大时不是在原内存空间末尾增大,而是把整个新数据开辟一个新的内存空间,然后释放原空间。

10.4.2vector构造函数

重生之——c++与我不得不说的一些事_第16张图片

#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;
}

10.4.3vector赋值操作

#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;
}

10.4.4vector容量和大小

重生之——c++与我不得不说的一些事_第17张图片

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;
}

10.4.5vector插入和删除

重生之——c++与我不得不说的一些事_第18张图片

 插入和删除的位置是:迭代器位置(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;
}

10.4.6vector数据读写

重生之——c++与我不得不说的一些事_第19张图片

#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;
}

10.4.7vector互换容器

实现两个容器内元素互换。且能够收缩内存空间

函数原型:swap(vec)  将vec与本身元素互换

注意事项:vector(v1)是创建匿名对象x,x根据v1大小创建合适对象。再利用swap(v1),也就是x和v1分别指向不同地方,而匿名对象x程序会自动释放

重生之——c++与我不得不说的一些事_第20张图片

#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;
}

10.4.8vector预留空间

作用:减少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;
}

10.5deque容器

10.5.1deque容器基本概念

作用:双端数组,可以对头和尾进行操作

deque和vector区别:(1)vector对于头部插入删除效率低,数据量越大,效率越低

                                   (2)deque插入删除比vector快

                                   (3)vector访问元素比deque快

工作原理:这是一个双端数组,在前和后插入删除时,如果预留空间满了,就会把数据存储到一个缓冲区,再通过一个中控器来存放数据的地址,因此该数组的值可能是非连续存放

重生之——c++与我不得不说的一些事_第21张图片

10.5.2deque构造函数

重生之——c++与我不得不说的一些事_第22张图片

#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;
}

10.5.3deque赋值操作

重生之——c++与我不得不说的一些事_第23张图片

#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;
}

10.5.4deque大小操作

重生之——c++与我不得不说的一些事_第24张图片

 注意事项: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;
}

10.5.5deque插入和和删除

重生之——c++与我不得不说的一些事_第25张图片

针对迭代器插入

#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;
}

10.5.6deque数据读写

重生之——c++与我不得不说的一些事_第26张图片

#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;
}

10.5.7deque排序

#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;
}

10.6stack容器

10.6.1stack基本概念

重生之——c++与我不得不说的一些事_第27张图片

 注意事项:(1)先进后出

                    (2)不能遍历栈,只能访问栈顶

10.6.2stack常用接口

重生之——c++与我不得不说的一些事_第28张图片

#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;
}

10.7queue容器

10.7.1queue基本概念

重生之——c++与我不得不说的一些事_第29张图片

注意事项:(1)queue=队列

                  (2)先进先出

                  (3)只有对队头和队尾可以访问,不能遍历

10.7.2queue常用接口

重生之——c++与我不得不说的一些事_第30张图片

#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;
}

10.8list容器

10.8.1list基本概念

重生之——c++与我不得不说的一些事_第31张图片

list容器=链表。链表由一系列结点构成。

结点:一个是数据域,一个是指针域

注意事项:(1)STL的链表是一个双向循环链表

                   (2)执行插入和删除不会造成原来迭代器失效

优点:(1)采用动态内存分配,不会造成浪费

           (2)执行插入和删除很方便,修改指针就行

缺点:(1)内存消耗大

10.8.2list构造函数

重生之——c++与我不得不说的一些事_第32张图片

#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;
}

10.8.3list赋值和操作

重生之——c++与我不得不说的一些事_第33张图片

#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;
}

10.8.4list大小操作

重生之——c++与我不得不说的一些事_第34张图片

#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;
}

10.8.5list插入和删除

重生之——c++与我不得不说的一些事_第35张图片

#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;
}

10.8.6list数据存取

注意事项:(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;
}

10.8.7list反转和排序

注意事项:(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;
}

10.9set和multiset容器

10.9.1set基本概念

作用:所有元素会在插入时自动排序(从小到大)

本质:set和multiset属于 关联式容器 ,底层是二叉树

10.9.2set构造和赋值

set容器特点:(1)输入无序,但输出有序(从小到大)

                       (2)set容器中的重复值会被抹去(multiset允许重复值)

重生之——c++与我不得不说的一些事_第36张图片

#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;
}

10.9.3set大小和交换

重生之——c++与我不得不说的一些事_第37张图片

 注意事项: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;
}

10.9.4set插入和删除

重生之——c++与我不得不说的一些事_第38张图片

#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;
}

10.9.5查找和统计

 注意事项:(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;
}

10.9.6set和multiset区别

重生之——c++与我不得不说的一些事_第39张图片

 注意事项:(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;
}

10.9.7pair队组创建

 队组的输出用:firstsecond

#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;
}

10.9.8set容器内置数据排序

#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;
}

10.9.9set容器自定义数据排序

#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;
}

10.10map和multimap容器

10.10.1map基本概念

重生之——c++与我不得不说的一些事_第40张图片

10.10.2map构造和赋值

重生之——c++与我不得不说的一些事_第41张图片

 注意事项:map中所有元素都是成对出现,插入数据时要使用对组

#include 
using namespace std;
#include

void PrintMap(map&m)
{
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << (*it).first << ' ' << it->second << endl;
	}
	cout << endl;
}

void test1()
{
	//默认构造
	mapm;
	//赋值 insert
	m.insert(pair(1, 10));
	m.insert(pair(3, 30));
	m.insert(pair(2, 20));
	m.insert(pair(4, 40));
	PrintMap(m);//会自动排序会升序
	//拷贝构造
	mapm2(m);
	PrintMap(m2);
	//赋值 =
	mapm3;
	m3 = m2;
	PrintMap(m3);
}

int main()
{
	test1();
	system("pause");
	return 0;
}

10.10.3map大小和交换

重生之——c++与我不得不说的一些事_第42张图片

#include 
using namespace std;
#include

void PrintMap(map& m)
{
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << (*it).first << ' ' << it->second << endl;
	}
	cout << endl;
}

void test1()
{
	//默认构造
	mapm;
	//赋值 insert
	m.insert(pair(1, 10));
	m.insert(pair(3, 30));
	m.insert(pair(2, 20));
	m.insert(pair(4, 40));
	//PrintMap(m);//会自动排序会升序
	//判断容器是否为空
	if (m.empty())
	{
		cout << "内容为空" << endl;
	}
	else
	{
		cout << "内容不为空" << endl;
		cout << m.size() << endl;
	}
	//交换
	mapm2;
	m2.insert(pair(1, 100));
	m2.insert(pair(3, 300));
	m2.insert(pair(2, 200));
	m2.insert(pair(4, 400));
	PrintMap(m);
	PrintMap(m2);
	//交换
	m2.swap(m);
	PrintMap(m);
	PrintMap(m2);
}

int main()
{
	test1();
	system("pause");
	return 0;
}

10.10.4map插入和删除

重生之——c++与我不得不说的一些事_第43张图片

    插入第一种 
    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

void PrintMap(map& m)
{
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << (*it).first << ' ' << it->second << endl;
	}
	cout << endl;
}

void test1()
{
	//默认构造
	mapm;
	//插入第一种 insert
	m.insert(pair(1, 10));
	//插入第2种 insert
	m.insert(make_pair(2, 20));
	//插入第3种 insert
	m.insert(map::value_type(4, 30));
	//插入第4种 insert  不推荐!!因为会容易误插。可以利用key访问value
	m[3] = 40;
	cout << m[2] << endl;
	PrintMap(m);//会自动排序会升序
	//删除 迭代器
	m.erase(m.begin());
	PrintMap(m);
	//删除 指定元素
	m.erase(3);
	PrintMap(m);
	//删除 区间
	m.erase(m.begin(), m.end());
	PrintMap(m);
	//清空
	m.clear();
	PrintMap(m);

}

int main()
{
	test1();
	system("pause");
	return 0;
}

10.10.5map查找和统计

 find:返回的是迭代器

count:map返回1和0   multimap任意返回

#include 
using namespace std;
#include

void PrintMap(map& m)
{
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << (*it).first << ' ' << it->second << endl;
	}
	cout << endl;
}

void test1()
{
	//默认构造
	mapm;
	m.insert(pair(1, 10));
	m.insert(pair(3, 20));
	m.insert(pair(3, 200));
	m.insert(pair(2, 30));
	m.insert(pair(4, 40));

	//查找
	map::iterator pos = m.find(2);
	if (pos != m.end())
	{
		cout << "找到了:" << (*pos).first << ' ' << pos->second << endl;
	}
	else
	{
		cout << "没找到" << endl;
	}
	//统计
	int num = m.count(3);//不允许插入 重复值,返回情况为 1 和 0.但是multimap可以
	cout << num << endl;


}

int main()
{
	test1();
	system("pause");
	return 0;
}

10.10.6map容器内置数据排序

#include 
using namespace std;
#include

class MyCompare
{
public:
	bool operator()(int v1, int v2)const
	{
		return v1 > v2;
	}

};

void test1()
{
	mapm;
	m.insert(make_pair(1, 10));
	m.insert(pair(2, 30));
	m.insert(make_pair(3, 100));
	m.insert(make_pair(4, 110));
	//输出
	for (map::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << (*it).first << ' ' << it->second << endl;
	}
	cout << endl;

}

int main()
{
	test1();
	system("pause");
	return 0;
}

11.1STL-函数对象

概念:(1)重载函数调用操作符的类,其对象称为函数对象

           (2)函数对象使用重载的()时,行为类似函数调用,也叫仿函数

本质:函数对象(仿函数)是一个,不是一个函数

11.1.1函数对象基本概念

11.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;
}

11.2谓词

11.2.1谓词概念

重生之——c++与我不得不说的一些事_第44张图片

11.2.2一元谓词

#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;
}

11.2.3二元谓词

#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;
}

11.3内建函数对象

11.3.1内建函数对象意义

在STL系统自带了一些仿函数,包括:算术关系逻辑

注意事项:使用时要加入头文件 <functional>

11.3.2算术仿函数

重生之——c++与我不得不说的一些事_第45张图片

 除 negate运算是一元运算,其它都是二元运算

#include 
using namespace std;
#include
void test1()
{
	//neagte 一元运算(取反)
	negaten;
	cout<m;
	cout << m(10, 20) << endl;
}

int main()
{
	test1();
	system("pause");
	return 0;
}

11.3.3关系仿函数

重生之——c++与我不得不说的一些事_第46张图片

 仿函数部分写: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;
}

11.3.4逻辑仿函数

重生之——c++与我不得不说的一些事_第47张图片

 注意事项: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;
}

12STL常用算法

12.1常用算法基本概念

重生之——c++与我不得不说的一些事_第48张图片

12.2常用遍历算法

12.2.1for_each

重生之——c++与我不得不说的一些事_第49张图片

 实现单纯的遍历

#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;
}

12.2.2transform

重生之——c++与我不得不说的一些事_第50张图片

 实现在容器数据搬移过程中再处理一下

#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;
}

12.3常用查找算法

重生之——c++与我不得不说的一些事_第51张图片

12.3.1find

重生之——c++与我不得不说的一些事_第52张图片

 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;
}

12.3.2find_if

重生之——c++与我不得不说的一些事_第53张图片

  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;
}

12.3.3adjacent_find

重生之——c++与我不得不说的一些事_第54张图片

#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;
}

重生之——c++与我不得不说的一些事_第55张图片

 容器数据必须有序,如果无序,则结果输出不一定正确

#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;
}

12.3.5count

重生之——c++与我不得不说的一些事_第56张图片

 (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;
}

12.3.6count_if

重生之——c++与我不得不说的一些事_第57张图片

#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;
}

12.4常用排序算法

重生之——c++与我不得不说的一些事_第58张图片

12.4.1sort

重生之——c++与我不得不说的一些事_第59张图片

 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

12.4.2random_shuffle

重生之——c++与我不得不说的一些事_第60张图片

#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

12.4.3merge

重生之——c++与我不得不说的一些事_第61张图片

 给目标容器分配内存空间是两个原容器大小之和

#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

12.4.5reverse

重生之——c++与我不得不说的一些事_第62张图片

#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

12.5常用拷贝和替换算法

重生之——c++与我不得不说的一些事_第63张图片

12.5.1copy

重生之——c++与我不得不说的一些事_第64张图片

#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;
}

12.5.2replace

重生之——c++与我不得不说的一些事_第65张图片

#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;
}

12.5.3replace_if

重生之——c++与我不得不说的一些事_第66张图片

#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;
}

12.5.4swap

重生之——c++与我不得不说的一些事_第67张图片

 交换的两个容器,数据类型要相同

#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;
}

12.6常用算术生成算法

重生之——c++与我不得不说的一些事_第68张图片

12.6.1accumulate

重生之——c++与我不得不说的一些事_第69张图片

 (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

12.6.2fill

重生之——c++与我不得不说的一些事_第70张图片

#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

12.7常用集合算法

重生之——c++与我不得不说的一些事_第71张图片

12.7.1set_intersection

重生之——c++与我不得不说的一些事_第72张图片

 (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;
}

12.7.2set_unin

重生之——c++与我不得不说的一些事_第73张图片

 (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;
}

12.7.3set_difference

重生之——c++与我不得不说的一些事_第74张图片

 (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

          对比次数:元素个数-排序轮数-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;
			}
		}

	}

2丶选择排序

原理:以数组中第一个数为最小值,然后利用一个 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;
			}
		}

你可能感兴趣的:(C++,c++,开发语言,后端)