3 - C++ 提高编程

主要针对 C++ 泛型编程STL 技术做详细讲解,探讨 C++ 更深层的应用。

一、模板

1、模板的概念

模板就是建立 通用的模具 ,大大提高 复用性

模板的特点

  • 模板不可以直接使用,它只是一个框架。

  • 模板的通用不是万能的。

模板的组成

  • template<> 开头,之后跟函数或者类。

  • <> 内为模板参数,一般使用 "class/typename",可以有多个参数,在调用模板的时候,可以在 <> 内添加具体的数据类型,即若未添加具体参数,则在调用的模板的时候是自动类型推导,而若添加了具体参数,就是指定类型推导。

  • template<class T> 这个 "T" 为一个通用数据类型,在使用函数模板的时候,会自动进行数据推导。

2、函数模板

  • C++ 另一种编程思想称为 泛型编程,主要利用的技术就是模板。

  • C++ 提供两种模板机制:函数模板类模板

函数模板语法

函数模板引用:

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

语法

template<typename T>
函数声明或定义

解释

template --- 声明创建模板。

typename --- 表示其后面的符号是一种数据类型,可以用 class 代替。

T --- 通用的数据类型,名称可以替换,通常为大写字母。

实例

#include<iostream>
using namespace std;

//交换函数
//交换整型
void swapInt(int& a, int& b) {
	int temp;
	temp = a;
	a = b;
	b = temp;
}
//交换浮点型
void swapDouble(double& a, double& b) {
	double temp;
	temp = a;
	a = b;
	b = temp;
}
//函数模板
//可以看到上述函数整体代码是相似的,只有数据类型不同,因此可以抽象成模板
template<typename T>	//声明一个模板,告诉编译器紧随其后的T不要报错,T是一个通用数据类型
void myswap(T& a, T& b) {
	T temp;
	temp = a;
	a = b;
	b = temp;
}

void test01() {
	int a = 10;
	int b = 20;
	float c = 3.14;
	float d = 6.28;

	//使用函数模板进行交换
	//两种方式使用
	//1、自动类型推导
	myswap(a, b);

	//2、显示指定类型
	myswap<float>(c, d);

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
	cout << "d = " << d << endl;
}


int main() {

	test01();

	system("pause");
	return 0;
}

函数模板注意事项

注意实现:

  • 自动类型推导,必须推导出一致的数据类型 T,才可以使用。

  • 模板必须要确定出 T 的数据类型,才可以使用。

  • 只有紧跟在模板之后的函数才属于模板内容,被称为模板函数,在此之后的函数与模板没有关系

实例

#include<iostream>
using namespace std;

//交换函数
//交换整型
void swapInt(int& a, int& b) {
	int temp;
	temp = a;
	a = b;
	b = temp;
}
//交换浮点型
void swapDouble(double& a, double& b) {
	double temp;
	temp = a;
	a = b;
	b = temp;
}
//函数模板
//可以看到上述函数整体代码是相似的,只有数据类型不同,因此可以抽象成模板
template<typename T>	//声明一个模板,告诉编译器紧随其后的T不要报错,T是一个通用数据类型
void myswap(T& a, T& b) {
	T temp;
	temp = a;
	a = b;
	b = temp;
}

template<class t>
void func() {
	cout << "func 正在被调用" << endl;
}

void test01() {
	int a = 10;
	int b = 20;
	float c = 3.14;
	float d = 6.28;

	//1、自动类型推导,必须推导出一致的数据类型T,才可以使用
	//myswap(a, c);		//错误,因为一个是int,一个是float

	//2、显示指定类型
	//func();		//错误,模板没有确定出具体的数据类型。
}


int main1() {

	test01();

	system("pause");
	return 0;
}

函数模板案例

案例

#include<iostream>
using namespace std;

template<class t>
void myswap3(t&a,t&b) {
	t temp;
	temp = a;
	a = b;
	b = temp;
}

template<class T>
void mysort(T array[], int len) {
	for (int i = 0; i < len; i++) {
		int max = i;		//认定最大值的下标
		for (int j = i + 1; j < len; j++) {
			if (array[i] < array[j]) {
				max = j;
			}
		}
		if (max != i) {
			//交换max和i元素
			myswap3(array[max], array[i]);
		}
	}
}

template<class T>
void printArray(T array[],int len) {
		cout <<"最大值为:"<< array[0] << endl;
}
void test03() {
	//测试char数组
	char Carray[] = "abcde";
	int len = sizeof(Carray) / sizeof(char);
	mysort(Carray, len);
	printArray(Carray, len);
}

void test003() {
	//测试int数组
	int array[] = { 1,2,5,4,3,6,7,9 };
	int len = sizeof(array) / sizeof(int);
	mysort(array, len);
	printArray(array, len);
}

int main() {

	//test03();
	test003();

	system("pause");
	return 0;
}

普通函数与函数模板的区别

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)。

  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换。

  • 如果利用显示指定类型的方式,可以发生隐式类型转换。

实例

#include<iostream>
using namespace std;

//普通函数调用时可以发生自动类型转换(隐式类型转换)
int myadd(int a, int b) {
	return a + b;
}

template<class T>
T myadd2(T a, T b) {
	return a + b;
}

void test4() {
	int a = 10,  b = 20;	
	char c = 'c';			//a-97   c-99
	cout << myadd(a, c) << endl;

	//函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
	//myadd2(a, c);			//报错,a和c不是同种类型
	
	//如果利用显示指定类型的方式,可以发生隐式类型转换
	myadd2<int>(a, c);		//正确,指定了T为int,即所有的实参都会被自动转换为int型。
	
}

int main4() {

	test4();

	system("pause");
	return 0;
}

普通函数与函数模板调用规则

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数。

  2. 可以通过空模板参数列表来强制调用函数模板。

  3. 函数模板也可以发生重载。

  4. 如果函数模板可以产生更好的匹配。优先调用函数模板。

  5. <> 内部为函数模板的参数,用来传入数据类型。

  6. 普通函数与函数模板最好不要同时出现。

实例

#include<iostream>
using namespace std;

void myprint(int a, int b) {
	cout << "普通函数正在被调用" << endl;
}

void myprint(int a, int b, int c) {
	cout << "普通函数重载正在被调用" << endl;
}

template<class T>
void myprint(T a, T b) {
	cout << "函数模板正在被调用" << endl;
}

template<class T>
void myprint(T a, T b, T c) {
	cout << "函数重载模板正在被调用" << endl;
}

void test5() {
	int a = 10, b = 20;
	char c = 'c';

	//如果函数模板和普通函数都可以实现,优先调用普通函数
	myprint(a, b);		//	普通函数正在被调用

	//可以通过空模板参数列表来强制调用函数模板
	myprint<>(a, b);	//	函数模板正在被调用

	//函数模板也可以发生重载
	myprint(a, b, c);	//	普通函数重载正在被调用
	myprint<int>(a, b, c);	//函数重载模板正在被调用

	//如果函数模板可以产生更好的匹配。优先调用函数模板
	char c1 = 1, c2 = 2;
	myprint(c1, c2);	//	函数模板正在被调用:因为使用普通函数转会有强制类型转换,而使用模板更合适
}


int main() {

	test5();

	system("pause");
	return 0;
}

模板的局限性

  • 模板的通用性并不是万能的

例如

template<class T>
void f(T a,T b){
    a = b;
}

在上述代码的赋值中,假如传入的参数是一个数组,就会无法实现。

再例如

template<class T>
void f(T a,T b){
    if(a>b){....}
}

在上述代码中,如果 T 的数据类型传入的是像 Person 这样的自定义数据类型,也无法正常运行。

因此 C++ 为了解决这些问题,提供模板的重载,可以为这些 特定的类型 提供 具体的模板

实例

#include<iostream>
using namespace std;

class Person {
public:
	Person(string name, int age)
	{
		this->age= age;
		this->name = name;
	}

	string name;
	int age;
};

template<class T>
bool myCompare(T &a, T &b) {
	if (a == b) {
		return true;
	}
	else
	{
		return false;
	}
}

//利用具体化Person的版本实现代码,具体化优先调用
template<>		//以template<>开头,不必设置<>内参数,对上述模板进行重载,意在假如传入的参数是					  Person数据类型的时候,优先调用具体化Person版本的重载函数
bool myCompare(Person& a, Person& b) {
	if (a.age == b.age && a.name == b.name) {
		return true;
	}
	else
	{
		return false;
	}
}


void test06() {
	//测试普通数据类型的对比
	int a = 10, b = 20;
	bool ret = myCompare(a, b);
	if (ret) {
		cout << "a==b" << endl;
	}
	else
	{
		cout << "a!=b" << endl;
	}
}

void test006() {
	//测试自定义数据类型的对比
	Person p1("Tom", 18), p2("Jerry", 20);
	bool ret = myCompare(p1, p2);		//在未出现第行代码的时候,会进行报错,因为无法判断自定义类型的判断标准
	if (ret) {
		cout << "p1==p2" << endl;
	}
	else {
		cout << "p1!=p2" << endl;
	}
}

int main() {

	test06();

	system("pause");
	return 0;
}

3、类模板

类模板语法

类模板作用

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

语法

template<class T>
class

实例

#include<iostream>
using namespace std;

template<class NameType,class AgeType>
class Person {
public:
	Person(NameType Name, AgeType age) {
		this->s_age = age;
		this->s_Name = Name;
	}

	void show() {
		cout << "Name:" << this->s_Name << " Age:" << this->s_age << endl;
	}

	NameType s_Name;
	AgeType s_age;
};

void test1() {
	Person<string, int> p1("孙悟空", 999);
	p1.show();
}

int main() {

	test1();

	system("pause");
	return 0;
}

类模板与函数模板的区别

区别

  • 函数模板中 template 之后跟的是函数,类模板之后跟的是类。

  • 类模板没有自动类型推导的使用方式。

  • 类模板在模板参数列表中可以有默认参数。

实例

#include<iostream>
using namespace std;

template<class NameType, class AgeType=int>		//第二个为默认参数
class Person2 {
public:
	Person2(NameType Name, AgeType age) {
		this->s_age = age;
		this->s_Name = Name;
	}

	void show() {
		cout << "Name:" << this->s_Name << " Age:" << this->s_age << endl;
	}

	NameType s_Name;
	AgeType s_age;
};

void test2() {
	Person2<string> p1("孙悟空", 999);
	p1.show();
}

int main() {

	test2();

	system("pause");
	return 0;
}

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

  • 普通类中的成员函数一开始就可以创建。

  • 类模板中的成员函数在调用时才能创建。

实例

#include<iostream>
using namespace std;

class Person1 {
public:
	void showPerson1() {
		cout << "Person1 show" << endl;
	}
};

class Person2 {
public:
	void showPerson2() {
		cout << "Person2 show" << endl;
	}
};

template<class T>
class myclass {
public:
	T obj;
	void func1() {
		obj.showPerson1();
	}
	void func2() {
		obj.showPerson2();
	}

};

void test3() {
	myclass<Person1>m;
	m.func1();
	myclass<Person2>mm;
	mm.func2();
}

int main() {

	test3();

	system("pause");
	return 0;
}

类模板对象做函数参数

目标 :类模板实例化的对象,向函数传参的方式。

一共有三种传入方式:

  1. 指定传入的类型 —— 直接显示成员的数据类型。

  2. 参数模板化 —— 将对象中的参数变为模板进行传递。

  3. 整个类模板化 —— 将这个对象类型模板化进行传递。

实例

#include<iostream>
using namespace std;

template<class T1,class T2>
class Person4 {
public:
	Person4(T1 name, T2 age) {
		this->s_Name = name;
		this->s_age = age;
	}

	T1 s_Name;
	T2 s_age;
};

//指定传入的类型——直接显示成员的数据类型
void myprint4(Person4<string, int>&p) {
	cout << "姓名:" << p.s_Name << " \t年龄:" << p.s_age << endl;
}
void test4() {
	Person4<string, int> p("孙悟空", 1000);
	myprint4(p);
}

//参数模板化——将对象中的参数变为模板进行传递
template<class T1,class T2>
void myprint04(Person4<T1, T2>& p) {
	cout << "姓名:" << p.s_Name << " \t年龄:" << p.s_age << endl;
	cout << "T1的数据类型为:" << typeid(T1).name() << endl;
	cout << "T2的数据类型为:" << typeid(T2).name() << endl;
}
void test04() {
	Person4<string, int> p("猪八戒", 500);
	myprint04(p);
}

//整个类模板化——将这个对象类型模板化进行传递
template<class T1>
void myprint004(T1& p) {
	cout << "姓名:" << p.s_Name << " \t年龄:" << p.s_age << endl;
	cout << "T1的数据类型为:" << typeid(T1).name() << endl;
}
void test004() {
	Person4<string, int> p("唐僧", 100);
	myprint004(p);
}

int main() {

	test4();
	test04();
	test004();

	system("pause");
	return 0;
}

类模板与继承

当类模板碰到继承时,需要注意以下几点:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型。

  • 如果不指定,编译器无法给子类分配内存。

  • 如果想要灵活指出父类中 T 的类型,子类也需变为类模板。

实例

#include<iostream>
using namespace std;

template<class T>
class Base {
public:
	T m;
};

//当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
class Son :public Base<int> {

};

//想要灵活指出父类中T的类型,子类也需变为类模板
template<class T1,class T2>
class Son1 :public Base<T2> {
public:
	Son1() {
		cout << "T1的数据类型为:" << typeid(T1).name() << endl;
		cout << "T2的数据类型为:" << typeid(T2).name() << endl;
	}
	T1 obj;
};

void test5() {
	Son1<int, char> obj;
}

int main() {

	test5();

	system("pause");
	return 0;
}

类模板成员函数类外实现

目标 :掌握类模板中的成员函数类外实现。

实例

#include<iostream>
using namespace std;

template<class T1, class T2>
class Person6 {
public:
	Person6(T1 Name, T2 age);

	void show();

	T1 s_Name;
	T2 s_age;
};

template<class T1,class T2>
Person6<T1, T2>::Person6(T1 Name, T2 age) {
	this->s_age = age;
	this->s_Name = Name;
}

template<class T1, class T2>
void Person6<T1, T2>::show() {
	cout << "Name:" << this->s_Name << " Age:" << this->s_age << endl;
}

void test6() {
	Person6<string, int>p("孙悟空",1000);
	p.show();
}

int main() {

	test6();

	system("pause");
	return 0;
}

类模板的分文件编写

问题

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

解决

  • 方法一:直接包含 .cpp 源文件。

  • 方法二:将声明和实现写到同一个文件中,并更改后缀名为 .hpp 文件,hpp 是约定俗称的,不是强制的。

实例

头文件 Person.hpp

#pragma once
#include<iostream>
using namespace std;

template<class T1, class T2>
class Person7 {
public:
	Person7(T1 Name, T2 age);

	void show();

	T1 s_Name;
	T2 s_age;
};

template<class T1, class T2>
Person7<T1, T2>::Person7(T1 Name, T2 age) {
	this->s_age = age;
	this->s_Name = Name;
}

template<class T1, class T2>
void Person7<T1, T2>::show() {
	cout << "Name:" << this->s_Name << " Age:" << this->s_age << endl;
}

源文件Person.cpp

#include<iostream>
using namespace std;

#include"Person.hpp"


void test7() {
	Person7<string, int>p("孙悟空", 1000);
	p.show();
}

int main() {

	test7();

	system("pause");
	return 0;
}

类模板与友元

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

全局函数类外实现—需要提前让编译器知道全局函数的存在。

4、模板理解

模板是 C++ 编程中的一个重要模式,它可以减少代码的重复,提高代码的复用性,在 C++ 中,模板分为自己创建的模板以及自带模板。

​当自己创建模板的时候,首先需要代码 template<class T>,并紧随其后写一个函数或者类,称为函数模板或者类模板,假如模板名称为 My,那么无论是函数模板还是类模板,在使用的时候,必须有 模板名尖括号 “<>”,其中尖括号内必须有指定数据类型,那么紧随其后传入的数据类型必须与指定的数据类型一致。

C++ 中也有语言自带的模板,例如容器 vector 就是一个类模板,在底层中是这样的。

template <class T>
class vector { }

​在使用的时候,vector<int> v,对应关系为 vector--template<int>--<class T>,模板名与 <参数类型> 是必不可少的。

二、STL 初识

1、STL 的诞生

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

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

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

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

2、STL 基本概念

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

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

  • 容器算法 之间通过 迭代器 进行无缝衔接。

  • STL 几乎所有的代码都采用了模板类或者模板函数。

3、STL 六大组件

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

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

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

  3. 迭代器 :扮演了容器与算法之间的胶合剂。

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

  5. 适配器 :一种用来修饰容器或者仿函数或迭代器接口的东西。

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

4、STL 中容器、算法、迭代器

容器 :置物之所也

STL 容器 就是将运用 最广泛的一些数据结构 实现出来。

常用的数据结构:数组、链表、树、栈、队列、集合、映射表等。

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

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

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

通俗来讲,容器就是用来存放数据的一片空间,一般容器中的元素数据类型都是相同的,容器可以存放内置的数据类型,例如 int,float 等等,也可以存放自定义的数据类型,例如自己创建的一个 Person 类,使用方法为: vector<数据类型> v

算法 :问题之解法也

有限的步骤,解决逻辑或数学上的问题,这一门学科称之为算法。

算法分为: 质变算法非质变算法

  • 质变算法 :是指运算过程中会更改区间内的元素的内容。例如拷贝、替换、删除等。

  • 非质变算法 :是指运算过程中不会更改区间内的元素内容。例如查找、计数、遍历、寻找极值等。

迭代器 :容器和算法之间粘合剂

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

每个容器都有自己专属的迭代器。

迭代器的使用非常类似于 指针,初学时候可以将其理解为指针。

迭代器种类:

种类 功能 支持运算
输入迭代器 对数据的只读访问 只读,支持++、==、!=
输出迭代器 对数据的只写访问 只写,支持++
前向迭代器 读写操作,并能向前推进迭代器 读写,支持++、==、!=
双向迭代器 读写操作,并能向前和向后操作 读写,支持++、--
随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 读写,支持++、--、[n]、-n、<、<=、>、>=

常用的容器中迭代器种类为双向迭代器,和随机访问迭代器。

5、容器算法迭代器初识

STL 中最常见的容器为 Vector,可以理解为数组。

vector 存放内置数据类型

容器:vector

算法:for_each(InputIterator first, InputIterator last, Function f)

  • first :表示要处理的范围的起始位置的迭代器。

  • last :表示要处理的范围的结束位置的迭代器(不包括这个位置的元素)。

  • f :表示要执行的操作,它是一个函数或函数对象,它接受范围内的元素作为参数,用于执行相应的操作。

  • for_each() 函数将会遍历指定范围内的所有元素,并依次调用 f 函数或函数对象,将每个元素作为参数传递给 f 进行处理。for_each() 并不会返回任何值,它只是对范围内的元素进行遍历和操作。

迭代器:vector<int>::iterator

实例

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>

void Myprint(int val) {
	cout << val << endl;
}

void test01() {
	vector<int> v;		//创建一个容器v

	v.push_back(10);	//使用尾插法将数据插入到容器v中
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	//遍历方法
	//方法一:利用迭代器创建指针
	vector<int>::iterator itBegin = v.begin();		//vector<int>::iterator是一个指针型的数据类型,此处相当于创建一个指针指向容器的第一个元素,而v.begin()是容器的内置方法,用于返回一个指向容器中第一个元素的迭代器
	vector<int>::iterator itEnd = v.end();

	while (itBegin != itEnd) {
		cout << *itBegin << endl;
		itBegin++;
	}

	//方法二
	//将上述方法一中代码写在一起
	for (vector<int>::iterator it=v.begin();it!= v.end(); it++) {
		cout << *it << endl;
	}

	//方法三
	//利用算法迭代
	for_each(v.begin(), v.end(), Myprint);		
}

int main() {

	test01();

	system("pause");
	return 0;
}

vector 中存放自定义数据类型

实例

#include<iostream>
using namespace std;
#include<vector>

class Person {
public:
	Person(string name, int age) {
		this->s_Name = name;
		this->s_Age = age;
	}

	string s_Name;
	int s_Age;
};

void test02() {
	vector<Person> v;

	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);


	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
		cout << "姓名:"<< (*it).s_Name << " 年龄:" << (*it).s_Age << endl;
	}


}

int main() {

	test02();

	system("pause");
	return 0;
}

三、STL - 常见容器

1、String 容器

基本概念

本质

  • stringC++ 风格的字符串,而 String 本质上是一个类。

Stringchar* 的区别

  • char * 是一个指针。

  • string 是一个类,类内部封装了 char *,管理这个字符串,是一个 char * 型的容器。

特点

string 类内部添加了很多成员方法。

例如:查找 find,拷贝 copy,删除 delete,替换 replace,插入 insert

string 管理 char * 所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责。

string 构造函数

构造函数原型

  • string() //创建一个空的字符串 例如:string str

  • string (const char * s) //使用字符串 s 初始化。

  • string (const string & str) //使用一个 string 对象初始化另一个 string 对象。

  • string (int n,char c) //使用 n 个字符 c 初始化。

实例

#include<iostream>
using namespace std;

/** 
string()							//创建一个空的字符串 例如:string  str
string (const char * s) 			//使用字符串s初始化
string (const string & str)			//使用一个string对象初始化另一个string对象
string (int n,char c)				//使用n个字符c初始化
*/

void test01() {
	string s1;	

	const char* str = "HelloWorld";
	string s2(str);
	cout << "s2 = " << s2 << endl;

	string s3(s2);
	cout << "s3=" << s3 << endl;

	string s4(10, 'a');
	cout << "s4 =" << s4 << endl;

}

int main() {

	test01();

	system("pause");
	return 0;
}

string 赋值操作

函数原型

  • string& operator=(const char * ) //char*类型字符串赋值给当前字符串。

  • string& operator=(const string &s) //把字符串s赋值给当前的字符串。

  • string& operator=(char c) //字符赋值给当前字符串。

  • string& assign(const char*s) //把字符串s赋值给当前的字符串。

  • string& assign(const char *s,int n) //把字符串s的前n个字符赋值给当前的字符串。

  • string& assign(const string& s) //把字符串s赋值给当前字符串。

  • string& assign(int n,char c) //用n个字符c赋给当前字符串。

实例

#include<iostream>
using namespace std;

/*
string& operator=(const char * )			//char*类型字符串赋值给当前字符串
string& operator=(const string &s)			//把字符串s赋值给当前的字符串
string& operator=(char c)					//字符赋值给当前字符串
string& assign(const char*s)				//把字符串s赋值给当前的字符串
string& assign(const char *s,int n)			//把字符串s的前n个字符赋值给当前的字符串
string& assign(const string& s)				//把字符串s赋值给当前字符串
string& assign(int n,char c)				//用n个字符c赋给当前字符串
*/

void test02() {
	string str1;
	str1 = "HelloWorld";
	cout << "str1 = " << str1 << endl;
	
	string str2;
	str2 = str1;
	cout << "str2 = " << str2 << endl;

	string str3;
	str3 = 'a';
	cout << "str3 = " << str3 << endl;

	string str4;
	str4.assign("Hello C++!");
	cout << "str4 = " << str4 << endl;

	string str5;
	str5.assign("Hello C++!", 5);
	cout << "str5 = " << str5 << endl;

	string str6;
	str6.assign(str5);
	cout << "str6 = " << str6 << endl;

	string str7;
	str7.assign(10,'w');
	cout << "str7 = " << str7 << endl;
}

int main() {

	test02();

	system("pause");
	return 0;
}

string 字符串拼接

  • string& operator+=(const char* str) //重载+=操作符。

  • string& operator+=(const char c) //重载+=操作符。

  • string& operator+=(const string& str) //重载+=操作符。

  • string& append(const char* s) //把字符串s连接到当前字符串末尾。

  • string& append(const char*s,int n) //把字符串s的前n个字符连接到当前字符串的末尾。

  • string& append(const string&s) //同operator+=(const stirng& str)

  • string& append(const string &s,int pos,int n) //字符串s中从pos开始的n个字符连接到字符串结尾。

实例

#include<iostream>
using namespace std;

/*
-string& operator+=(const char* str)			//重载+=操作符
-string& operator+=(const char c)				//重载+=操作符
-string& operator+=(const string& str)			//重载+=操作符
-string& append(const char* s)					//把字符串s连接到当前字符串末尾
-string& append(const char*s,int n)				//把字符串s的前n个字符连接到当前字符串的末尾
-string& append(const string&s)					//同operator+=(const stirng& str)
-string& append(const string &s,int pos,int n)	//字符串s中从pos开始的n个字符连接到字符串结尾
*/

void test03() {
	string str1 = "我";
	str1 += "爱玩游戏";
	cout << "str1 = " << str1 << endl;

	str1 += ':';
	cout << "str1 = " << str1 << endl;

	string str2 = "LOL DNF";
	str1 += str2;
	cout << "str1 = " << str1 << endl;

	string str3 = "I";
	str3.append(" love");
	cout << "str3 = " << str3 << endl;

	str3.append(" game ,abcde", 5);
	cout << "str3 = " << str3 << endl;

	str3.append(str2);
	cout << "str3 = " << str3 << endl;

	str3.append(str2, 0, 3);
	cout << "str3 = " << str3 << endl;
}

int main() {

	test03();

	system("pause");
	return 0;
}

string 查找和替换

功能描述

  • 查找 :查找指定字符串是否存在。

  • 替换 :在指定的位置替换字符串。

函数原型

  • int find(const string& str,int pos=0)const //查找 string 第一次出现的位置,从 pos 开始查找。

  • int find(const char* s,int pos=0)const //查找 s 第一次出现的位置,从 pos 开始查找。

  • int find(const char* s,int pos=0,int n)const //从 pos 位置查找 s 的前 n 个字符第一次位置。

  • int find(const char c,int pos=0)const //查找 c 第一次出现的位置,从 pos 开始查找。

  • int rfind(const string& str,int pos=npos)const //查找 string 最后一次位置,从 pos 开始查找。

  • int rfind(const char* s,int pos=npos)const //查找 s 最后一次位置,从 pos 开始查找。

  • int rfind(const char* s,int pos,int n)const //从 pos 位置查找 s 的前 n 个字符最后一次位置。

  • int rfind(const char c,int pos=0)const //查找 c 最后一次出现的位置,从 pos 开始查找。

  • string& replace(int pos,int n,const string& str) //替换从 pos 开始 n 个字符为字符串 str

  • string& replace(int pos,int n,const char *s) //替换从 pos 开始 n 个字符为字符串 s

实例

#include<iostream>
using namespace std;

/*
-int find(const string& str,int pos=0)const				//查找`string`第一次出现的位置,从`pos`开始查找
-int find(const char* s,int pos=0)const					//查找`s`第一次出现的位置,从`pos`开始查找
-int find(const char* s,int pos=0,int n)const			//从`pos`位置查找`s`的前`n`个字符第一次位置
-int find(const char c,int pos=0)const					//查找`c`第一次出现的位置,从`pos`开始查找
-int rfind(const string& str,int pos=npos)const			//查找`string`最后一次位置,从`pos`开始查找
-int rfind(const char* s,int pos=npos)const				//查找`s`最后一次位置,从`pos`开始查找
-int rfind(const char* s,int pos,int n)const			//从`pos`位置查找`s`的前`n`个字符最后一次位置
-int rfind(const char c,int pos=0)const					//查找`c`最后一次出现的位置,从`pos`开始查找
-string& replace(int pos,int n,const string& str)		//替换从`pos`开始`n`个字符为字符串`str`
-string& replace(int pos,int n,const char *s)			//替换从`pos`开始`n`个字符为字符串`s`
*/

void mprint(int &pos) {
	if (pos == -1) {
		cout << "未找到字符串" << endl;
	}
	else
	{
		cout << "找到字符串,pos = " << pos << endl;
	}
}

//查找
void test04() {
	//find:从左往右查找
	string str1 = "abcdefgde";
	int pos = str1.find("de");
	mprint(pos);

	//rfind:从右往左查找
	pos = str1.rfind("de");
	mprint(pos);
}

//替换
void test004() {
	string str1 = "abcdefg";
	str1.replace(1, 3, "11111");		//意思为将字符串从1号下标开始到3号下标结束,替换成指定字符串,无论指定字符串是否长度比原先长度长
	cout << "str1=" << str1 << endl;
}

int main() {

	test04();
	test004();

	system("pause");
	return 0;
}

string 字符串比较

比较方式

  • 字符串比较是按 ASCALL 码进行对比。

    = 返回1 > 返回1 <返回-1

函数原型

  • int compare(const string &s)const //与字符串 s 比较。

  • int compare(const char *s)const //与字符串 s 比较。

实例 :

#include<iostream>
using namespace std;

/*
-int compare(const string &s)const				//与字符串`s`比较
-int compare(const char *s)const				//与字符串`s`比较
*/

void test05() {
	string str1 = "Hello";
	int rel = str1.compare("H");
	cout << rel << endl;
}

int main() {

	test05();

	system("pause");
	return 0;
}

string 字符存取

  • char & operator[] (int n) //通过 [] 取字符。

  • char & at(int n) //通过 at 取字符。

实例

#include<iostream>
using namespace std;

/*
-char & operator[] (int n)		//通过`[]`取字符
-char & at(int n)				//通过`at`取字符
*/

void test06() {
	string str = "Hello";

	for (int i=0; i < str.size(); i++) {
		cout << str[i] << " ";
	}

	cout << endl;

	for (int i=0; i < str.size(); i++) {
		cout << str.at(i) << " ";
	}
	cout << endl;

	//修改单个字符
	str[0] = 'x';
	cout << "str=" << str << endl;

	str.at(1) = 'x';
	cout << "str=" << str << endl;
}

int main() {

	test06();

	system("pause");
	return 0;
}

string 插入和删除

函数原型

  • string& insert(int pos,const char* s) //插入字符串。

  • string& insert(int pos,const string & str) //插入字符串。

  • string& insert(int pos,int n,char c) //在指定位置插入 n 个字符 c

  • string& erase(int pos,int n = npos) //删除从 pos 开始的 n 个字符。

实例

#include<iostream>
using namespace std;

/*
-string& insert(int pos,const char* s)			//插入字符串
-string& insert(int pos,const string & str)		//插入字符串
-string& insert(int pos,int n,char c)			//在指定位置插入`n`个字符`c`
-string& erase(int pos,int n = npos)			//删除从`pos`开始的`n`个字符
*/

void test07() {
	string str1 = "hello";

	str1.insert(1, "elel");
	cout << "str1 = " << str1 << endl;

	str1.erase(1, 4);
	cout << "str1 = " << str1 << endl;
}

int main() {

	test07();

	system("pause");
	return 0;
}

string 子串

函数原型

  • string substr(int pos =0,int n = npos)const //返回由pos开始的n个字符组成的字符串。

实例

#include<iostream>
using namespace std;

//string substr(int pos =0,int n = npos)const			//返回由`pos`开始的`n`个字符组成的字符串

void test08() {
	string str = "李四@.com";
	cout << "strSub = " << str.substr(0, 4) << endl;
}

void test008() {
	string str = "lisi@.com";
	int pos = str.find('@');

	cout << "strSub = " << str.substr(0, pos) << endl;
}

int main() {

	test08();
	test008();

	system("pause");
	return 0;
}

2、vector 容器

基本概念

功能

  • vector 数据结构和数组非常相似,也称为单端数组。

区别

  • 不同之处在于数组是静态空间,而 vector 可以动态扩展。

动态扩展

  • 并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。

  • vector 容器的迭代器是支持随机访问的迭代器。

vector 构造函数

函数原型

  • vector <T> v //采用模板实现类实现,默认构造函数。

  • vector (v.begin(),v.end()) //将 v[begin(),end()) 前闭后开区间中的元素拷贝给本身。

  • vector (n,elem) //构造函数将 nelem 拷贝给本身。

  • vector (const vector &vec) //拷贝构造函数。

实例

#include<iostream>
using namespace std;
#include<vector>

/*
-vector <T> v						//采用模板实现类实现,默认构造函数
-vector (v.begin(),v.end())			//将`v[begin(),end())`前闭后开区间中的元素拷贝给本身
-vector (n,elem)					//构造函数将`n`个`elem`拷贝给本身
-vector (const vector &vec)			//拷贝构造函数
*/

void printVector(vector<int>&v) {
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {

	vector<int>v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}

	printVector(v1);

	//通过区间方式进行构造
	vector<int>v2(v1.begin(), v1.end());
	printVector(v2);

	//n个elem方式构造
	vector<int>v3(10, 100);
	printVector(v3);

	//拷贝构造
	vector<int>v4(v3);
	printVector(v4);
}

int main() {

	test01();

	system("pause");
	return 0;
}

vector 赋值操作

函数原型

  • vector& operator=(const vector& vec) //重载等号操作符。

  • assign(beg,end) //将 [beg,end) 区间中的数据拷贝赋值给本身。

  • assign(n,elem) //将 nelem 拷贝给本身。

实例

#include<iostream>
using namespace std;
#include"printVector.h"

/*
-vector& operator=(const vector& vec)		//重载等号操作符
-assign(beg,end)							//将`[beg,end)`区间中的数据拷贝赋值给本身
-assign(n,elem)								//将`n`个`elem`拷贝给本身
*/

void test02() {
	vector<int>v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);

	vector<int>v2;
	v2 = v1;
	printVector(v2);

	vector<int>v3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);

	vector<int>v4;
	v4.assign(10, 100);
	printVector(v4);

}

int main() {

	test02();

	system("pause");
	return 0;
}

vector 容量和大小

函数原型

  • empty() //判断容器是否为空。

  • capacity() //容器的容量。

  • size() //返回容器中元素的个数。

  • resize(int num) //重新指定容器的长度为 num,若容器变长,则以默认值填充新位置,如果容器变短则末尾超出容器长度的元素被删除。

  • resize(int num,elem) //重新指定容器的长度为 num,若容器变长,则以 elem 填充新位置,如果容器变短则末尾超出容器长度的元素被删除。

实例

#include<iostream>
using namespace std;
#include"printVector.h"

/*
-empty()					//判断容器是否为空
-capacity()					//容器的容量
-size()						//返回容器中元素的个数
-resize(int num)			//重新指定容器的长度为`num`,若容器变长,则以默认值填充新位置,如果容器变短则末尾超出容器长度的元素被删除
-resize(int num,elem)		//重新指定容器的长度为`num`,若容器变长,则以`elem`填充新位置,如果容器变短则末尾超出容器长度的元素被删除
*/

void test03() {
	vector<int> v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);
	if (v1.empty()) {
		cout << "容器为空" << endl;
	}
	else
	{
		cout << "容器不为空" << endl;
		cout << "容器的容量为:" << v1.capacity() << endl;
		cout << "容器的大小为:" << v1.size() << endl;
	}

	v1.resize(15);			//重新指定容器长度
	printVector(v1);
	v1.resize(16, 2);		//重新指定容器长度,并将多余位置用指定数字填充,默认为0
	printVector(v1);
	v1.resize(5);			//重新指定容器长度,如果变小了,会将多余的元素删除
	printVector(v1);
	cout << "重新指定长度之后的大小为:" << v1.size() << endl;
}

int main() {

	test03();

	system("pause");
	return 0;
}

vector 插入和删除

函数原型

  • push_back(ele) //尾部插入元素 ele

  • pop_back(ele) //删除最后一个元素。

  • insert(const_iterator pos,ele) //迭代器指向位置 pos 插入元素 ele

  • insert(const_iterator pos,int count,ele) //迭代器指向位置 pos 插入 count 个元素 ele

  • erase(const_iterator pos) //删除迭代器指向的元素。

  • erase(const_iterator start,const_iterator end) //删除迭代器从 startend 之间的元素。

  • clear() //删除容器中所有元素。

实例

#include<iostream>
using namespace std;
#include"printVector.h"

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

void test04() {
	vector<int>v1;

	//尾插
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	printVector(v1);

	//尾删
	v1.pop_back();
	printVector(v1);

	//插入
	v1.insert(v1.begin(), 1000);
	printVector(v1);
	v1.insert(v1.begin(), 2, 1000);		//可以指定数目
	printVector(v1);
	
	//删除
	v1.erase(v1.begin(),v1.end()-2);
	printVector(v1);

	//清空
	v1.clear();
	printVector(v1);


}

int main() {

	test04();

	system("pause");
	return 0;
}

vector 数据存取

函数原型

  • at(int idx) //返回索引 idx 所指的数据。

  • operator[] //返回索引 idx 所指的数据。

  • front() //返回容器中第一个数据元素。

  • back() //返回容器中最后一个数据元素。

实例

#include<iostream>
using namespace std;
#include"printVector.h"

/*
-at(int idx)		//返回索引`idx`所指的数据
-operator[]			//返回索引`idx`所指的数据
-front()			//返回容器中第一个数据元素
-back()				//返回容器中最后一个数据元素
*/

void test05() {
	vector<int>v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}

	for (int i = 0; i < v1.size(); i++) {
		cout << v1[i] << " ";
	}
	cout << endl;

	for (int i = 0; i < v1.size(); i++) {
		cout << v1.at(i) << " ";
	}
	cout << endl;

	//返回第一个元素
	cout << "容器中第一个元素为:" << v1.front() << endl;

	//返回最后一个元素
	cout << "容器中最后一个元素为:" << v1.back() << endl;
}

int main() {

	test05();

	system("pause");
	return 0;
}

vector 容器互换

功能描述

  • 实现两个容器内元素互换。

函数原型

  • swap(vec) //将 vec 与本身的元素互换。

实例

#include<iostream>
using namespace std;
#include"printVector.h"

/*
swap(vec)			//将`vec`与本身的元素互换
*/

void test06() {
	vector<int >v1;
	v1.push_back(10);
	v1.push_back(20);

	cout << "互换之前v1 = " ;
	printVector(v1);

	vector<int>v2;
	v2.push_back(30);
	cout << "互换之前v2=";
	printVector(v2);

	v1.swap(v2);
	cout << "互换之后v1 = ";
	printVector(v1);
	cout << "互换之后v2=";
	printVector(v2);

}

int main() {

	test06();

	system("pause");
	return 0;
}

vector 预留空间

功能描述

  • 减少 vector 在动态扩展容量时的扩散次数。

函数原型

  • reserve(int len) //容器中预留 len 个元素长度,预留位置不初始化,元素不可访问。

实例 :

#include<iostream>
using namespace std;
#include"printVector.h"

/*
reserve(int len)			//容器中预留`len`个元素长度,预留位置不初始化,元素不可访问
*/

void test07() {
	vector<int>v;
	v.reserve(100000);
	
	int num=0;	//统计动态开辟多少次
	int* p = NULL;
	for (int i = 0; i < 100000; i++) {
		v.push_back(i);

		if (p != &v[0]) {
			p = &v[0];
			num++;
		}
	}

	cout << "动态开辟了" << num << "次" << endl;
}

int main() {

	test07();

	system("pause");
	return 0;
}

3、deque 容器

deque 容器基本概念

功能

  • 双端数组,可以对头端进行插入删除操作。

dequevector 区别

  • vector 对于头部的插入删除效率低,数据量越大,效率越低。

  • deque 相对而言,对头部的插入删除速度会比 vector 快。

  • vector 访问元素时的速度会比 deque 快,这和两者内部实现有关。

deque 构造函数

  • vector 一样,如果有需要,可以使用const来限定容器是只读状态。

deque 赋值操作

  • vector 一样。

deque 大小操作

  • vector一样,区别在于,deque 没有容量的概念。

deque 插入和删除

函数原型

两端插入操作:

  • push_back(elem) //在容器尾部添加一个数据。

  • push_front(elem) //在容器头部添加一个数据。

  • pop_back(elem) //删除容器最后一个数据。

  • pop_front(elem) //删除容器第一个数据。

指定位置操作:

  • insert(pos,elem) //在 pos 位置插入一个 elem 元素的拷贝,返回新数据的位置。

  • insert(pos,n,elem) //在 pos 位置插入 nelem 元素的拷贝,无返回值。

  • insert(pos,begin,end) //在 pos 位置插入 [beg,end] 区间的数据,无返回值。

  • clear() //清空容器内的所有数据。

  • erase(beg,end) //删除 [beg,end) 区间的数据,返回下一个数据的位置。

  • erase(pos) //删除 pos 位置的数据,返回下一个位置的数据。

实例

#include<iostream>
using namespace std;
#include"print.h"

/*
两端插入操作:

-push_back(elem)			//在容器尾部添加一个数据
-push_front(elem)			//在容器头部添加一个数据
-pop_back(elem)				//删除容器最后一个数据
-pop_back(elem)				//删除容器第一个数据

指定位置操作:

-insert(pos,elem)			//在`pos`位置插入一个`elem`元素的拷贝,返回新数据的位置
-insert(pos,n,elem)			//在`pos`位置插入`n`个`elem`元素的拷贝,无返回值
-insert(pos,begin,end)		//在`pos`位置插入`[beg,end]`区间的数据,无返回值
-clear()`					//清空容器内的所有数据
-erase(beg,end)				//删除`[beg,end)`区间的数据,返回下一个数据的位置
-erase(pos)					//删除`pos`位置的数据,返回下一个位置的数据
*/

void test01() {
	deque<int>d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);

	printDeque(d);

	//头删
	d.pop_front();
	printDeque(d);

	//尾删
	d.pop_back();
	printDeque(d);
}

void test001() {
	deque<int>d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);

	printDeque(d);

	//insert插入
	d.insert(d.begin(), 1000);
	printDeque(d);

	d.insert(d.begin(), 2, 10000);
	printDeque(d);

	//按照区间进行插入
	deque<int>d2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);

	d.insert(d.begin(), d2.begin(), d2.end());
	printDeque(d);
}

void test0001() {
	deque<int>d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);

	printDeque(d);

	//删除
	//d.erase(d.begin());
	deque<int>::iterator it = d.begin();
	it++;
	d.erase(it);
	printDeque(d);

	//按区间进行删除
	
	d.erase(d.begin(),d.end());		//=d.clear();
	printDeque(d);
}

int main() {

	test01();
	test001();
	test0001();

	system("pause");
	return 0;
}

deque 数据存取

  • vector一样。

deque 排序

功能描述

  • 利用算法实现对 deque 容器进行排序。

算法

  • sort(iterator beg,iterator end) //对 begend 区间内元素进行排序。

实例

#include<iostream>
using namespace std;
#include<algorithm>
#include"print.h"

void test02() {
	deque<int>d;
	d.push_back(10);
	d.push_back(233);
	d.push_back(75);
	d.push_back(135);
	d.push_back(223);
	printDeque(d);

	sort(d.begin(), d.end());
	printDeque(d);
}

int main() {

	test02();

	system("pause");
	return 0;
}

4、stack 容器

stack 基本概念

概念stack 是一种先进后出的数据结构,它只有一个出口。

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

栈中进入数据称为 —— 入栈 push

栈中弹出数据称为 —— 出栈 pop

stack 常用接口

功能描述 :栈容器对外常用接口。

构造函数

  • stack<T> stk //stack 采用模板类实现,stack 的默认构造形式。

  • stack(const stack &stk) //拷贝构造函数。

赋值操作

  • stack & operator=(const stack &stk) //重载等号操作符。

数据存取

  • push(elem) //向栈顶添加元素。

  • pop() //从栈顶移除第一个元素。

  • top() //返回栈顶元素。

大小操作

  • empty() //判断堆栈是否为空。

  • size() //返回栈的大小。

实例

#include<iostream>
using namespace std;
#include<stack>

/*
构造函数:

	-stack<T> stk				//`stack`采用模板类实现,`stack`的默认构造形式
	-stack(const stack &stk)	//拷贝构造函数

赋值操作:

	-stack & operator=(const stack &stk)			//重载等号操作符

数据存取:

	-push(elem)			//向栈顶添加元素
	-pop()				//从栈顶移除第一个元素
	-top()				//返回栈顶元素

大小操作:

	-empty()			//判断堆栈是否为空
	-size()				//返回栈的大小
*/

void test() {
	stack<int> s;
	s.push(10);
	s.push(20);
	s.push(30);
	s.push(40);

	s.pop();
	stack<int> s2;
	s2 = s;

	while (!s.empty()) {
		cout << "s栈顶元素为:" << s.top() << endl;
		cout << "s栈的大小为:" << s.size() << endl;
		s.pop();
	}

	while (!s2.empty()) {
		cout << "s2栈顶元素为:" << s2.top() << endl;
		cout << "s2栈的大小为:" << s2.size() << endl;
		s2.pop();
	}


}

int main() {

	test();

	system("pause");
	return 0;
}

5、queue 容器

queue 基本概念

概念 :queue 是一种 先进先出 的数据结果,有两个出口。

队列允许从一端新增元素,从另一端移除元素。

队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为。

queue 常用接口

构造函数

  • queue<T> que //queue 采用模板类实现,queue 对象的默认构造形式。

  • queue(const queue &que) //拷贝构造函数。

赋值操作

  • queue& operator=(const queue &que) //重载等号操作符。

数据存取

  • push(elem) //往队尾添加元素。

  • pop() //从队头移除第一个元素。

  • back() //返回最后一个元素。

  • front() //返回第一个元素。

大小操作

  • empty() //判断队列是否为空。

  • size() //返回队列的大小。

实例

#include<iostream>
using namespace std;
#include<queue>

/*
构造函数:

	-queue<T> que						//`queue`采用模板类实现,`queue`对象的默认构造形式
	-queue(const  queue  &que)			//拷贝构造函数

赋值操作:

	-queue& operator=(const queue &que)			//重载等号操作符

数据存取:

	-push(elem)			//往队尾添加元素
	-pop()				//从队头移除第一个元素
	-back()				//返回最后一个元素
	-front()			//返回第一个元素

大小操作:

	-empty()			//判断队列是否为空
	-size()				//返回队列的大小
*/

void test() {
	queue<int> q;
	q.push(10);
	q.push(20);
	q.push(30);
	q.push(40);

	queue<int> q2;
	q2 = q;

	while (!q.empty()) {
		cout << "队列第一个元素为: " << q.front() << endl;
		cout << "队列最后一个元素为:" << q.back() << endl;
		cout << "队列的大小为: " << q.size() << endl;

		q.pop();
		cout << endl;
	}

}

int main() {

	test();

	system("pause");
	return 0;
}

6、list 容器

list 基本概念

功能 :将数据进行链式存储。

链表 :是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。

组成 :链表由一系列节点组成。

节点的组成 :一个是存储数据元素的 数据域 ,另一个是存储下一个节点的 指针域

list 构造函数

函数原型

  • list<T> lst //list 采用模板类实现对象的默认构造形式。

  • list(beg,end) //构造函数将 [beg,end] 区间中的元素拷贝给自身。

  • list(n,elem) //构造函数将 nelem 拷贝给自身。

  • list(const list &list) //拷贝构造函数。

实例

#include<iostream>
using namespace std;
#include"printList.h"

/*
-list<T> lst				//`list`采用模板类实现对象的默认构造形式
-list(beg,end)				//构造函数将`[beg,end]`区间中的元素拷贝给自身
-list(n,elem)				//构造函数将`n`个`elem`拷贝给自身
-list(const list &list)		//拷贝构造函数
*/

void test01() {
	list<int> L;
	L.push_back(10);
	L.push_back(20);
	L.push_back(30);
	L.push_back(40);
	printList(L);

	list<int>L2(L.begin(),L.end());
	printList(L2);

	list<int>L3(L2);
	printList(L3);

	list<int>L4(4,100);
	printList(L4);
	
}

int main() {

	test01();

	system("pause");
	return 0;
}

list 赋值和交换

函数原型

  • assign(beg,end) //将 [beg,end] 区间中的数据拷贝赋值给本身。

  • assign(n,elem) //将 nelem 拷贝赋值给本身。

  • list& operator=(const list &lst) //重载等号操作符。

  • swap(lst) //将 lst 与本身的元素互换。

实例

#include<iostream>
using namespace std;
#include"printList.h"

/*
-assign(beg,end)						//将`[beg,end]`区间中的数据拷贝赋值给本身
-assign(n,elem)							//将`n`个`elem`拷贝赋值给本身
-list& operator=(const list &lst)		//重载等号操作符
-swap(lst)								//将`lst`与本身的元素互换
*/

void test02() {
	list<int> L;
	L.push_back(10);
	L.push_back(20);
	L.push_back(30);
	L.push_back(40);
	printList(L);

	list<int>L2;
	L2.assign(L.begin(), L.end());
	printList(L2);

	list<int>L3;
	L3.swap(L2);
	printList(L3);
	printList(L2);

}

int main() {

	test02();

	system("pause");
	return 0;
}

list 大小操作

函数原型

  • empty() //判断容器是否为空。

  • size() //返回容器中元素的个数。

  • resize(int num) //重新指定容器的长度为 num,若容器变长,则以默认值填充新位置,如果容器变短则末尾超出容器长度的元素被删除。

  • resize(int num,elem) //重新指定容器的长度为 num,若容器变长,则以 elem 填充新位置,如果容器变短则末尾超出容器长度的元素被删除。

list 插入和删除

函数原型

  • push_back(ele) //尾部插入元素 ele

  • pop_back(ele) //删除最后一个元素。

  • push_front(ele) //头部插入元素 ele

  • pop_front(ele) //从容器开头移除一个元素。

  • insert(const_iterator pos,ele) //迭代器指向位置 pos 插入元素 ele,返回新数据的位置。

  • insert(const_iterator pos,int count,ele) //迭代器指向位置 pos 插入 count 个元素 ele,无返回值。

  • insert(pos,beg,end) //迭代器指向位置 pos 插入 [beg,end] 区间中的元素,无返回值。

  • erase(const_iterator pos) //删除 pos 位置的元素,返回下一个数据的位置。

  • erase(beg,end) //删除迭代器从 begend 之间的元素,返回下一个数据的位置。

  • clear() //删除容器中所有元素。

  • remove(elem) //删除容器中所有与 elem 值匹配的元素。

7、set/multiset 容器

基本概念

简介

  • 所有元素都会在插入时自动被排序。

本质

  • set/multiset 属于 关联式容器 ,底层结构是用 二叉树 实现。

setmultiset 区别

  • set 不允许容器中有重复的元素。

  • multiset 允许容器中有重复的元素。

8、map/multimap 容器

基本概念

简介

  • map 中所有元素都是 pair(队组),即成对出现的数据。

  • pair 中第一个元素为 key(键值),起到索引作用,第二个元素为 value(实值)。

  • 所有元素都会根据元素的键值自动排序。

本质

  • map/multimap 属于 关联式容器 ,底层结构是用二叉树实现。

优点

  • 可以根据 key 值快速找到 value 值。

mapmultimap 区别

  • map 不允许容器中有重复 key 值元素。

  • multimap 中允许有重复 key 值元素。

构造结构

  • 创建容器:map<数据类型,数据类型> m;

  • 插入数据:m.insert(pair<int,int>(1,10))

四、STL - 函数对象

1、函数对象

概念

概念

  • 重载 函数调用操作符 的类,其对象称为 函数对象
  • 函数对象 使用重载的 () 时,行为类似函数调用,称为 仿函数

本质

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

函数对象使用

特点

  • 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值。

  • 函数对象超出普通函数的概念,函数对象可以有自己的状态。

  • 函数对象可以作为参数传递。

2、内建函数对象

概念

  • STL 内建了一些函数对象。

分类

  • 算数仿函数

  • 关系仿函数

  • 逻辑仿函数

用法

  • 这些仿函数所产生的对象,用法和一般函数完全相同。

  • 使用内建函数对象,需要引入头文件 #include<functional>

3、函数对象意义

​在使用算法的时候,由于普通函数无法作为参数运用到算法的参数列表中,因此,诞生了函数对象,通过类内对运算符的重载,实现相应的功能,此时由该类创建的对象不仅拥有函数的功能,同时也可以作为参数运用到算法中,其优点还有更多,例如函数对象拥有普通函数所不拥有的状态等等。

五、STL - 常用算法

概述

  • 算法主要是由头文件 <algorithm><functional><numeric> 组成。

  • <algorithm> 是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等。

  • <numeric> 体积很小,只包括在几个序列上进行简单数学运算的模板函数。

  • <functional> 定义了一些模板类,用以声明函数对象。

1、常用遍历算法

  • for_each()for_each(起始迭代器,结束迭代器,函数/函数对象)

  • transform :搬运容器到另一个容器中 transform(原容器起始迭代器,原容器结束迭代器,目标容器起始迭代器,函数/函数对象),目标容器需要提前开辟空间,targert_v.resize(v.size())

2、常用查找算法

  • find() :查找指定元素,返回值类型为迭代器 find(iterator beg,iterator end,value)

  • find_if() :按条件查找元素,返回值类型为迭代器 find(iterator beg,iterator end,_Pred)_Pred 为谓词。

  • adjacent_find() :查找相邻重复元素 adjacent_find(iterator beg,iterator end)

  • find() :查找指定元素是否存在,返回值类型为bool binary_search(iterator beg,iterator end,value),必须为有序序列。

  • count() :统计元素出现次数 count(iterator beg,iterator end,value)

  • count_if() :按条件统计元素出现次数 count_if(iterator beg,iterator end,_Pred)

六、STL 总结

STL 中,包括三个重要组成部分:容器、迭代器、算法

  • 容器 :主要功能就是用于存放各种类型的数据,包括类,容器的种类有很多,分别拥有不同的功能,依据需要选择不同的容器来进行数据的存放,容器主要包括:String、vector、deque、stack、queue、list、set/multiset、map/multiset

    • 首先需要创建一个容器:容器类型<数据类型>容器名,例如 vector<int> v
    • 其次需要进行数据的存入:容器名.push_back(),需要注意的是,括号里面的数据类型需要与创建容器时所设的数据类型一致。
  • 迭代器 :当拥有一个容器的时候,如何获取容器内每个数据,与获取数组数据用到指针一样,在容器内部拥有属于自己可以直接使用的”指针“,即迭代器,迭代器充当指针用来依次读取容器内每一个数据的位置。

    • 容器名<数据类型>::iterator,它是一个数据类型,用于声明一个指针,此时这个指针就可以自由移动,可以使用 auto 关键字进行自动推导数据类型。
    • 容器有内置的获取第一个元素位置的方法 v.begin(),同理最后一个元素位置为 v.end()
    • 使用迭代器可以遍历容器内的每一个元素。
  • 算法

  • 函数对象 :分为自定义数据类型以及内建数据类型。

    • 内建数据类型是自带的函数对象,可以进行直接使用而不需要进行自己定义。
    • 自定义是由自己创建的函数对象,主要用作实现不同的功能。
    • 方法 :在一个类的内部进行对括号的重载 operator ()(参数列表),在重载的时候,如果参数列表中只有一个元素称为一元谓词,两个称为二元谓词,在重载之后,由于是在类内重载,因此在算法使用的时候,有的算法需要传入的是函数对象,此时需要将谓词转化为函数对象,即使用类创建一个匿名对象。
    • 注意 :使用函数对象的方法是,创建一个类,在这个类的内部重载运算符,以达到某种目的,由这种类所生成的对象称为函数对象,其主要用于在算法中作为参数以实现某种目的。
posted @ 2025-12-31 14:06  Amireux77  阅读(2)  评论(0)    收藏  举报