Cpp中另一种编程思想称为泛型编程,主要利用的技术是模板,Cpp提供两种模板机制函数模板和类模板。模板就是建立通用的模具,大大提高程序的复用性。
函数模板是通用的函数描述,函数模板使用泛型来定义函数,其中的泛型可用具体的类型(如double和int)替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。这种方式也被称为通用编程。创建函数模板需要使用关键字==template和关键字typename==。
函数模板的作用是,建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来表示。模板的创建语法:
template <typename T> //也可以使用class代替typename
函数声明或定义
解释:
template,声明创建模板;typename,表示其后面的符号是一种数据类型,可以使用class来替换;、T,通用的数据类型,名称可以替换,通常为大写字母。
函数模板的使用有两种方式:
- 自动类型推导;
- 显示指定类型,语法:
函数名<参数类型>(参数列表)
示例:
template <typename T>
void Swap(T &a, T &b) //定义了一个交换两对象值的函数
{
T temp = a;
a = b;
b = temp;
}
//函数模板的使用
int num1 = 10;
int num2 = 20;
Swap(num1, num2); //1.自动类型推导
Swap<int>(num1, num2); //2.显示指定类型
函数模板使用的注意事项:
- 自动类型推导,必须推导出一致的数据类型
T,才可以使用;- 模板必须要确定出
T的数据类型,才可以使用。
示例:
//函数模板完成数组的排序
#include
using namespace std;
template <class T>
void Swap(T& a, T& b) //模板交换函数
{
T temp = a;
a = b;
b = temp;
}
template <class T>
void SelectSort(T* arr, int len) //选择排序
{
for (int i = 0; i < len - 1; i++)
{
int max = i;
for (int j = i; j < len; j++)
{
if (arr[max] < arr[j])
{
max = j;
}
}
if (max != i)
{
Swap(arr[max], arr[i]);
}
}
}
template <typename T> //模板显示函数
void showArray(T* arr, int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
char arr1[] = "helloworld!";
int num = sizeof(arr1) / sizeof(arr1[0]);
SelectSort(arr1, num);
showArray(arr1, num);
int arr2[] = { 1, 2, 5, 8, 9, 4, 3, 10, 7, 6};
int num2 = sizeof(arr2) / sizeof(arr2[0]);
SelectSort(arr2, num2);
showArray(arr2, num2);
}
int main(void)
{
test01();
return 0;
}
普通函数和函数模板的区别:
- 普通函数调用时可以发生自动类型转换(隐式类型转换);
- 函数模板在调用时,如果利用自动类型推导,不会发生自动类型转换;
- 如果利用显示指定类型的方式,可以发生隐式类型转换。
示例:
//普通函数和模板函数的区别
#include
using namespace std;
int myAdd01(int num1, int num2)
{
return num1 + num2;
}
template <class T>
T myAdd02(T num1, T num2)
{
return num1 + num2;
}
void test01()
{
int a = 10;
int b = 10;
char c = 'c'; // c- 99
//1.普通函数进行隐式类型转换
cout << myAdd01(a, c) << endl;
//2.模板函数不能完成隐式类型转换
//cout << myAdd02(a, c) << endl;
//3.显示指定类型可以完成隐式类型转换
cout << myAdd02<int>(a, c) << endl;
}
int main(void)
{
test01();
return 0;
}
总结:建议使用显示指定类型的方式,去调用函数模板,因为这样可以自己指定通用类型T。
普通函数和函数模板的调用规则:
- 如果函数模板和普通函数都可以实现,优先调用普通函数;
- 可以通过空模板参数列表来强制调用函数模板,空模板是指
<>中的内容为空,语法:函数名<>(参数列表);- 函数模板也可以发生重载;
- 如果函数模板可以产生更好的匹配优先调用函数模板。
示例:
// 普通函数和函数模板的调用规则
#include
using namespace std;
void myPrint(int a, int b)
{
cout << "普通函数的调用" << endl;
}
template <class T>
void myPrint(T a, T b)
{
cout << "函数模板的调用" << endl;
}
template <typename T>
void myPrint(T a, T b, T c) //函数模板的重载
{
cout << "函数模板重载的调用" << endl;
}
void test01()
{
int a = 10;
int b = 10;
//1.优先调用普通函数
myPrint(a, b);
//2.可以通过空模板参数列表强制调用函数模板
myPrint<>(a, b);
//3.函数模板可以发生重载
myPrint(a, b, 100);
//4.函数模板可以产生更好的匹配,优先调用函数模板
char c = 'c';
myPrint(c, c);
}
int main(void)
{
test01();
return 0;
}
函数模板的局限性,函数模板的通用性并不是万能的,比如对于自定义类型Person,函数模板是不具有通用性的。因此Cpp为了解决这类问题,提供了模板的重载,可以为这些特定的类型提供具体化的模板。具体化是,显示具体化的原型和定义是以template<>开头,语法就是在重载的函数模板的函数定义前加上==tempalte<>==。并通过名称来指出类型,具体化的方式优于常规模板。示例:
//模板的局限性
#include
#include
using namespace std;
template <typename T>
bool myCompare(T& a, T& b)
{
if (a == b)
return true;
else
return false;
}
void myPrint(bool flag)
{
if (flag == true)
cout << "两者相等" << endl;
else
cout << "两者不等" << endl;
}
class Person
{
public:
int m_Age;
string m_Name;
Person(string name, int age)
{
this->m_Age = age;
this->m_Name = name;
}
};
//具体化,为特定类型提供函数模板
template<> bool myCompare(Person& p1, Person& p2)
{
if (p1.m_Age == p2.m_Age and p1.m_Name == p2.m_Name)
return true;
else
return false;
}
void test01()
{
int a = 10;
int b = 10;
myPrint(myCompare(a, b));
}
void test02()
{
Person p1("Tom", 10);
Person p2("Tom", 10);
myPrint(myCompare(p1, p2));
}
int main(void)
{
test01();
test02();
return 0;
}
类模板的作用,是建立一个通用的类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。语法:
template <typename T> //也可以使用class代替typename
类
解析:
template,声明创建模板;typename,表明其后面的符号是一种数据类型,可以用class来代替;T,通用的数据类型,名称可以替换,通常为大写字母;- 类模板需要用显示指定的方式来指定模板参数列表
<>,并且只能通过显示指定类型的方式进行使用。
示例:
//类模板
#include
#include
using namespace std;
template <class NameType, class AgeType> //类模板
class Person
{
public:
NameType m_Name;
AgeType m_Age;
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void ShowInfo()
{
cout << this->m_Name << endl;
cout << this->m_Age << endl;
}
};
void test01()
{
Person<string, int> p1("孙悟空", 999); //显示指定模板参数列表
p1.ShowInfo();
}
int main(void)
{
test01();
return 0;
}
总结:类模板和函数模板非常相似,在声明类模板template后面加类,此类称为类模板。
类模板和函数模板的区别:
- 类模板没有自动类型推导的使用方式,只能显示指定类型;
- 类模板在模板参数列表中可以有默认参数。
示例:
//类模板的使用
#include
using namespace std;
template <class NameType, class AgeType = int >
class Person
{
public:
NameType m_Name;
AgeType m_Age;
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void ShowInfo()
{
cout << "姓名:" << this->m_Name << endl;
cout << "年龄:" << this->m_Age << endl;
}
};
void test01()
{
//Person p("hello", 10); //无法自动推导类型
Person <string, double> p2("hello", 22.0);
p2.ShowInfo();
Person <string> p3("world", 22); //age的默认类型为int
p3.ShowInfo();
}
int main(void)
{
test01();
return 0;
}
类模板中成员函数的创建时机,类模板中成员函数和普通类中成员函数创建时机是有区别的:
- 普通类中的成员函数一开始就可以创建;
- 类模板中的成员函数在调用时才创建。
类模板对象作为函数参数,类模板实例化的对象,向函数传参的方式,一共有三种传入方式:
- 指定传入的类型——直接显示对象的数据类型(最常用的方式);
- 参数模板化——将对象中的参数变为模板进行传递;
- 整个类模板化——将这个对象类型模板进行传递。
示例:
// 类模板对象作为函数的参数
#include
using namespace std;
template <class T1, class T2>
class Person
{
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
void ShowInfo()
{
cout << "姓名:" << this->m_Name << endl;
cout << "年龄:" << this->m_Age << endl;
}
};
//1.类模板的对象做函数的参数,并且采用引用参数的方式
void printPerson1(Person <string, int>&p)
{
p.ShowInfo();
}
//2.参数模板化
template <typename T1, typename T2>
void printPerson2(Person<T1, T2>& p)
{
p.ShowInfo();
cout << "T1的类型为:" << typeid(T1).name() << endl; //查看类型名称
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
//3.整个类模板化
template <class T>
void printPerson3(T &p)
{
p.ShowInfo();
cout << "T的类型为:" << typeid(T).name() << endl;
}
void test01()
{
Person<string, int> p("孙悟空", 1000);
printPerson1(p);
Person <string, int> p2("猪八戒", 999);
printPerson2(p2);
Person <string, int> p3("唐僧", 998);
printPerson3(p3);
}
int main(void)
{
test01();
return 0;
}
总结:
- 通过类模板创建的对象,可以有三种方式向函数中进行传参;
- 使用比较广泛的是第一种,指定传入的类型;
- 使用
typeid(类型).name(),查看指定类型的字符串表示。
类模板与继承,当类模板碰到继承时,需要注意:
- 当子类继承的父类是一个类模板时,子类在声明的时候,要制定父类中
T的类型;- 如果不指定,编译器无法给子类分配内存;
- 如果想灵活指定父类中
T的类型,子类也需变为类模板。
示例:
//类模板与继承
#include
using namespace std;
template <class T>
class Base
{
public:
T m_base;
Base()
{
cout << "Base中T的数据类型为:" << typeid(T).name() << endl;
}
};
//class Son : public Base //错误的继承,因为必须知道T的类型才能确定Son分配的字节大小
//1.明确指定父类T的类型的继承
class Son1 :public Base <int>
{
public:
Son1()
{
cout << "Son1中的数据类型固定为int" << endl;
}
};
//2.更改父类T的类型的继承
//如果想灵活指定父类中T的类型,子类也需要类模板
template <class T1, class T2>
class Son2 :public Base <T2>
{
public:
T1 m_obj;
Son2()
{
cout << "Son2中T1的数据类型为:" << typeid(T1).name() << endl;
cout << "Son2中T2的数据类型为:" << typeid(T2).name() << endl;
}
};
void test01()
{
Son1 s1;
cout << "-------------------------" << endl;
Son2<int, char> s2;
}
int main(void)
{
test01();
return 0;
}
总结:如果父类是类模板,那么子类在继承的过程中需要明确指定出父类中T的类型。
类模板成员函数的类外实现,在类外实现类模板的成员函数的时候,需要在类名的后面加上模板参数列表,语法是:template<模板参数列表>; 类名<模板参数列表>::成员函数实现,示例:
//类模板成员函数类外实现
#include
#include
using namespace std;
template <class T1, class T2>
class Person
{
public:
T1 m_Age;
T2 m_Name;
//成员函数的声明
Person(T2 name, T1 age);
void ShowInfo();
};
//1.构造函数的类外实现
template <class T1, class T2>
Person<T1, T2>::Person(T2 name, T1 age)
{
this->m_Age = age;
this->m_Name = name;
}
//2.成员函数的类外实现
template <class T1, class T2>
void Person<T1, T2>::ShowInfo() //加上模板参数列表
{
cout << "姓名:" << this->m_Name
<< "年龄:" << this->m_Age << endl;
}
void test01()
{
Person<int, string> p("Tom", 22);
p.ShowInfo();
}
int main(void)
{
test01();
return 0;
}
类模板份文件编写,产生的问题是类模板中成员函数创建时机是在调用阶段,导致文件编写时链接不到。解决的方法是:
- 解决方法1:直接包含
.cpp源文件;- 解决方法2:将声明和实现写到同一个文件中,并更改后缀名为
.hpp,hpp是约定的名称,并不是强制的。
示例:
person.hpp中代码:
#pragma once
#include
#include
using namespace std;
template <class T1, class T2>
class Person
{
public:
T1 m_Age;
T2 m_Name;
Person(T1 age, T2 name);
void ShowInfo();
};
template <class T1, class T2>
Person<T1, T2>::Person(T1 age, T2 name)
{
this->m_Age = age;
this->m_Name = name;
}
template <class T1, class T2>
void Person<T1, T2>::ShowInfo()
{
cout << "姓名:" << this->m_Name
<< " 年龄:" << this->m_Age << endl;
}
person.h中的代码:
#pragma once
#include
#include
using namespace std;
template <class T1, class T2>
class Person
{
public:
T1 m_Age;
T2 m_Name;
Person(T1 age, T2 name);
void ShowInfo();
};
person.cpp中的代码:
#include "person.h"
template
Person::Person(T1 age, T2 name)
{
this->m_Age = age;
this->m_Name = name;
}
template
void Person::ShowInfo()
{
cout << "姓名:" << this->m_Name
<< " 年龄:" << this->m_Age << endl;
}
main.cpp中的代码:
//类模板分文件编写问题及解决方式
#include
#include
//1.解决方式:直接包含源文件
//#include "person.cpp"
//2.解决方法:将.h和.cpp中的内容写到一起,将后缀改为.hpp文件
#include "person.hpp"
void test01()
{
Person<int, string> p(22, "Tom");
p.ShowInfo();
}
int main(void)
{
test01();
return 0;
}
类模板与友元,需要掌握类模板配合友元函数的类内和类外实现:
- 全局函数类内实现,直接在类内声明友元即可;
- 全局函数类外实现,需要提前让编译器知道全局函数的存在。
示例:
//类模板与友元
#include
#include
using namespace std;
//提前让编译器知道Person类的存在
template <class T1, class T2>
class Person;
//提前让编译器知道该全局函数的存在
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p);
//实现Person模板类
template <class T1, class T2>
class Person
{
//1.全局函数的类内实现
friend void printPerson1(Person <T1, T2> &p)
{
cout << "姓名:" << p.m_Name
<< " 年龄:" << p.m_Age << endl;
}
//2.全局函数类外实现,加上空模板的参数列表(必须)
//如果全局函数是类外实现的,那么需要编译器提前知道全局函数的存在
friend void printPerson2<>(Person<T1, T2>& p);
private:
T1 m_Name;
T2 m_Age;
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
};
//2.全局函数的类外实现
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
cout << "姓名:" << p.m_Name
<< " 年龄:" << p.m_Age << endl;
}
void test01()
{
Person<string, int> p("Jane", 22);
printPerson1(p);
printPerson2(p);
}
int main(void)
{
test01();
return 0;
}
类模板案例,实现一个通用的数组,
myarray.hpp内容:
//类模板案例,实现一个通用的数组
#pragma once
#include
using namespace std;
template <class T>
class MyArray
{
private:
T* pAddress; //指针指向堆区开辟的空间
int m_Capacity; //存放数组容量
int m_Size; //存放数组中的数据个数
public:
//有参构造函数
MyArray(int capacity)
{
//cout << "MyArray的有参构造" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddress = new T[capacity];
}
//析构函数
~MyArray()
{
//cout << "MyArray的析构函数" << endl;
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
}
}
//拷贝构造函数
MyArray(const MyArray &arr)
{
//cout << "MyArray的拷贝构造" << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity]; //深拷贝
for (int i = 0; i < arr.m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
//重载运算符 =
MyArray& operator=(const MyArray& arr)
{
//cout << "operator=的调用" << endl;
if (this->pAddress != NULL) //如果以前存在数据则先进行释放
{
delete[] this->pAddress;
this->pAddress = NULL;
}
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity]; //深拷贝
for (int i = 0; i < arr.m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this; //返回自身的引用
}
//尾插法
void insert_back(const T &value)
{
if (this->m_Size == this->m_Capacity)
{
cout << "容量已满" << endl;
return;
}
this->pAddress[this->m_Size] = value; //在数组尾部插入元素
this->m_Size++; //数组长度+1
}
//尾删法
void delete_back()
{
if (this->m_Size == 0)
{
return;
}
this->m_Size--; //访问不到最后一个元素,逻辑尾删
}
//头插法
void insert_head(const T& value)
{
if (this->m_Size == this->m_Capacity)
{
cout << "容量已满" << endl;
return;
}
for (int i = this->m_Size; i>=0; i--)
{
this->pAddress[i + 1] = this->pAddress[i];
}
*this->pAddress = value; //在数组头部插入元素
this->m_Size++; //数组长度+1
}
//头删法
void delete_head()
{
if (this->m_Size == 0)
{
return;
}
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = this->pAddress[i + 1];
}
this->m_Size--;
}
//通过下标来进行访问,重载运算符[]
T& operator[](int idx)
{
return this->pAddress[idx];
}
//返回容量大小
int getCapacity()
{
return this->m_Capacity;
}
//返回现存的大小
int getSize()
{
return this->m_Size;
}
};
class Person
{
public:
string m_Name;
int m_Age;
Person() {}
Person(string name, int age)
{
this->m_Age = age;
this->m_Name = name;
}
};
main.cpp的内容:
#include "myarray.hpp"
void printArrayInt(MyArray<int>& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
//测试
MyArray<int> arr1(11);
for (int i = 0; i < 5; i++)
{
arr1.insert_back(i); //尾插法
}
printArrayInt(arr1);
cout << "arr1[2] = " << arr1[2] << endl; //测试运算符[]
for (int i = 0; i < 5; i++)
{
arr1.insert_head(i); //头插法
}
printArrayInt(arr1);
cout << "arr1[5] = " << arr1[5] << endl; //测试运算符[]
arr1.delete_back(); //尾删法
printArrayInt(arr1);
arr1.delete_head(); //头删法
printArrayInt(arr1);
}
void printArrayPerson(MyArray<Person>& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << "姓名:" << arr[i].m_Name
<< " 年龄:" << arr[i].m_Age << endl;
}
cout << "---------------------" << endl;
}
void test02()
{
MyArray<Person> arr1(10);
Person p1("Tom", 22);
Person p2("Alice", 23);
Person p3("Mike", 22);
arr1.insert_back(p1);
arr1.insert_back(p1);
arr1.insert_back(p2);
arr1.insert_back(p3);
printArrayPerson(arr1);
arr1.insert_head(p2);
printArrayPerson(arr1);
arr1.delete_back();
printArrayPerson(arr1);
arr1.delete_head();
printArrayPerson(arr1);
}
int main(void)
{
//test01();
test02();
return 0;
Cpp的面向对象和泛型编程思想,目的就是复用性的提升,大多数情况下,数据结构和算法都未能有统一的标准,为了建立数据结构和算法的标准,从而诞生了STL。
STL的基本概念:
- STL(Standard Template Library,标准模板库);
- STL从广义上分为,容器(container)、算法(algorithm)和迭代器(iterator);
- 容器和算法之间通过迭代器进行无缝连接;
- STL几乎所有的代码都采用了模板类或者模板函数。
STL的六大组件:STL大致分为六大组件,分别是容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
1、容器:各种数据结构,如
vector、list、deque、set、map等,用于存放数据;2、算法:各种常用的算法,如
sort、find、copy、for_each等;3、迭代器:扮演了容器与算法之间的胶合剂;
4、仿函数:行为类似函数,可以作为算法的某种策略;
5、适配器:一种用来修饰容器或者仿函数或迭代器接口的东西;
6、空间配置器:负责空间的配置和管理。
容器(Container):置物之所,STL容器就是将运用最广泛的一些数据结构实现出来,常用的数据结构有:数组、链表、树、栈、队列、集合、映射等。这些容器又分为序列式容器和关联式容器两种。
- 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置;
- 关联式容器:是二叉树的结构,各元素之间没有严格的物理上的顺序关系。
算法(Algorithm):问题之解法,通过有限的步骤,解决逻辑上或数学上的问题,算法分为质变算法和非质变算法两种。
- 质变算法:是指运算过程中会更改区间内的元素的内容,例如拷贝、替换、删除等;
- 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历等。
若需要使用STL中的算法,则必须包含STL中算法的头文件,#include。
迭代器(Iterator):容器和算法之间粘合剂,迭代器提供一种方法,使之能够依序寻访某个容器所含的各个元素,而有无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器。可以将迭代器理解为指针。
迭代器的种类:
| 种类 | 功能 | 支持运算符 |
|---|---|---|
| 输入迭代器 | 对数据的只读访问 | 只读、支持++、==、!= |
| 输出迭代器 | 对数据的只写访问 | 只写、支持++ |
| 前向迭代器 | 读写操作、并能向前推进迭代器 | 读写、支持++、==、!= |
| 双向迭代器 | 读写操作、并能向前和向后操作 | 读写、支持++ |
| 随机访问迭代器 | 读写操作、可以以跳跃的方式访问任意的数据、功能最强的迭代器 | 读写、支持`++、–、[n]、-n、<、<=、>、>= |
常用的容器中迭代器种类为双向迭代器、随机访问迭代器。
vector容器容器:
vector头文件:
#include算法:
for_each迭代器:
vector::iterator
vector数据结构和数据非常相似,也称为单端数组,单端数组顾名思义是在数组的尾端进行插入和删除,vector与普通数组的区别在于处在数组是静态空间,而verctor可以动态扩展,动态扩展并不是在原有空间的基础上续接新空间,而是寻找更大的内存空间,然后将原数组拷贝到新空间,并且释放原空间。vector容器的迭代器是支持随机访问的迭代器。

vector容器的简单使用:
遍历vector容器的方式示例:
//vector容器存放内置数据类型
#include
#include
#include
using namespace std;
void myprint(int val)
{
cout << val << endl;
}
void test01()
{
vector<int> v; //默认构造,创建数据类型为int的vector容器
v.push_back(10); //尾插法插入数据
v.push_back(20);
v.push_back(30);
v.push_back(40);
//第一种遍历方式
//vector::iterator itBegin = v.begin();
//vector::iterator itEnd = v.end();
//while (itBegin != itEnd)
//{
// cout << *itBegin << endl;
// itBegin++;
//}
//第二种遍历方式使用for循环(推荐)
//for (vector::iterator it = v.begin(); it != v.end(); it++)
//{
// cout << *it << endl;
//}
//第三种遍历方式,利用STL中提供的遍历算法(for_each),需包含头文件,并且提供进行操作的函数
for_each(v.begin(), v.end(), &myprint);
}
int main(void)
{
test01();
return 0;
}
vector容器存放自定义数据类型的示例:
//vector容器存放自定义数据类型
#include
#include
#include
#include
using namespace std;
class Person
{
public:
string m_Name;
int m_Age;
Person() {}
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
};
//1.存放自定义数据类型
void test01()
{
vector<Person> v;
Person p1("Tom", 22);
Person p2("Alice", 22);
Person p3("Jane", 22);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
//cout << "姓名:" << (*it).m_Name << "\t年龄:" << (*it).m_Age << endl;
cout << "姓名:" << it->m_Name
<< "\t年龄:" << it->m_Age << endl;
}
}
//2.存放自定义数据类型的指针
void test02()
{
vector<Person*> v;
Person* ptr1 = new Person("Tom", 22);
Person* ptr2 = new Person("Alice", 22);
Person* ptr3 = new Person("Hellen", 22);
v.push_back(ptr1);
v.push_back(ptr2);
v.push_back(ptr3);
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "姓名:" << (*it)->m_Name << "\t年龄:" << (*it)->m_Age << endl;
}
}
int main(void)
{
//test01();
test02();
return 0;
}
vector容器中再嵌套vector容器示例:
//vector容器中嵌套容器
#include
#include
#include
using namespace std;
void test01()
{
vector <vector<int>> V; //创建大容器
vector <int> v1; //创建小容器
vector <int> v2;
vector <int> v3;
for (int i = 0; i < 4; i++) //向小容器中添加数据
{
v1.push_back(i+1);
v2.push_back(i+5);
v3.push_back(i+9);
}
V.push_back(v1); //向大容器中添加数据
V.push_back(v2);
V.push_back(v3);
for (vector<vector<int>>::iterator It = V.begin(); It != V.end(); It++) //外层迭代器指针
{
// (*It) 是 vector类型
for (vector<int>::iterator it = (*It).begin(); it != (*It).end(); it++) //内层迭代器指针
{
cout << *it << " ";
}
cout << endl;
}
}
int main(void)
{
test01();
return 0;
}
vector的构造函数,函数原型:
vector//采用模板实现类实现,默认构造函数;v; vector(itertor beg, iterator end);//将[beg, end)区间内的元素拷贝给vector,左闭右开;vector (n, elem);//构造函数将n个elem拷贝给v本身;vector (const vector& vec);//拷贝构造函数,利用vec来构造vctor。
示例:
//vector容器构造
#include
#include
using namespace std;
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//1.默认构造
vector <int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i + 1);
}
printVector(v1);
//2.通过区间的方式构造
vector<int> v2(v1.begin(), v1.end());
printVector(v2);
//3.将n个elem拷贝给本身构造
vector<int> v3(5, 3);
printVector(v3);
//4.拷贝构造
vector<int> v4(v3);
printVector(v4);
}
int main(void)
{
test01();
return 0;
}
vector赋值操作,函数原型:
vector& operator=(const vector &vec);//重载=操作符,进行赋值;assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身;assign(n, elem);//将n个elem拷贝赋值给本身。
示例:
//vector的赋值操作
#include
#include
using namespace std;
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 + 1);
}
printVector(v1);
//赋值 operator=
vector <int> v2 = v1;
printVector(v2);
//assign
vector <int> v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
//n个elem
vector<int> v4;
v4.assign(4, 1);
printVector(v4);
}
int main(void)
{
test01();
return 0;
}
vector容器大小操作,函数原型:
empty();//判断容器是否为空;capacity();//返回容器的容量;size();//返回容器中元素的个数;resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值0填充新的位置;若容器变短,则末尾超出容器长度的元素被删除;resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem填充新位置;若容器变短,则末尾超出容器长度的元素被删除。
示例:
//vector容器容量和大小的操作
#include
#include
using namespace std;
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int> v;
cout << v.empty() << endl; //如果为空输出1,不为空输出0
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.pop_back();
printVector(v);
cout << v.capacity() << endl; //返回容量
cout << v.size() << endl; //返回当前元素个数
v.resize(6);
printVector(v);
v.resize(8, 2); //用2来填充
printVector(v);
}
int main(void)
{
test01();
return 0;
}
vector插入和删除,函数原型:
push_back(elem);//尾插法插入元素elem;pop_back();//尾部弹出最后一个元素;insert(const_iterator pos, elem);//迭代器指向位置pos插入元素elem;insert(const_iterator pos, int count, elem);//迭代器指向位置pos插入count个元素elem;erase(const_iterator pos);//删除迭代器指向的元素;erase(const_iterator start, const_iterator end);//删除迭代器[start, end)之间的元素;clear();//删除容器中所有的元素。
示例:
//vector容器的插入和删除操作
#include
#include
using namespace std;
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int> v;
//尾插法
v.push_back(10);
v.push_back(10);
v.push_back(10);
v.push_back(10);
printVector(v);
//尾删法
v.pop_back();
printVector(v);
//插入 第一个参数是迭代器
v.insert(v.begin(), 20);
printVector(v);
v.insert(v.end(), 2, 30);
printVector(v);
//删除 第一个参数是迭代器
v.erase(v.begin());
printVector(v);
v.erase(v.begin(), v.end()); //与clear()一致
}
int main(void)
{
test01();
return 0;
}
vector数据存取,函数原型:
operator[](int idx);//重载[]运算符,返回索引idx所指的元素;at(int idx);//返回索引idx所指的数据;front();//返回容器中第一个数据元素;back();//返回容器中最后一个数据元素。
示例:
//vector容器的存取
#include
#include
using namespace std;
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector <int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i + 1);
}
printVector(v);
//取元素
cout << v[2] << endl;
cout << v.at(3) << endl;
cout << v.front() << endl;
cout << v.back() << endl;
}
int main(void)
{
test01();
return 0;
}
vector互换容器,功能是实现两个容器内元素的互换操作,函数原型:
swap(vec);//将vec与本身的元素进行互换。
示例:
//vector容器的互换
#include
#include
using namespace std;
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//1.基本使用
void test01()
{
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i + 1);
}
printVector(v1);
vector<int> v2;
for (int i = 10; i > 0; i--)
{
v2.push_back(i);
}
printVector(v2);
v2.swap(v1);
cout << "交换后" << endl;
printVector(v1);
printVector(v2);
}
//2.实际用途,巧用swap可以收缩内存空间
void test02()
{
vector<int> v1;
for (int i = 0; i < 100000; i++)
{
v1.push_back(i + 1);
}
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为:" << v1.size() << endl;
v1.resize(3, 0); //重新指定大小后,大小变化,但是容量不变
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为:" << v1.size() << endl;
//巧用swap来收缩内存空间
//vector(v1)相当于调用拷贝构造函数,来创建了一个匿名对象x
//然后根据v1的大小和容量来创建了这个匿名对象,这个对象的大小和容量都是3
//再使用swap进行交换v1和匿名对象的空间,匿名对象在使用完毕后就被系统回收,这样就收缩了v1的空间
vector<int>(v1).swap(v1);
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为:" << v1.size() << endl;
}
int main(void)
{
//test01();
test02();
return 0;
}
vector预留空间,功能是减少vector在动态扩展容量时的扩展次数,函数原型:
reserve(int len);//容器预留len哥元素长度,预留位置不被初始化,元素也不可被访问。
示例:
//vector容器预留空间
#include
#include
using namespace std;
void test01()
{
vector<int> v1;
int num1 = 0; //统计开辟空间的次数
int* p1 = NULL;
for (int i = 0; i < 100000; i++)
{
v1.push_back(i + 1);
if (p1 != &v1[0])
{
p1 = &v1[0];
num1++;
}
}
cout << "num1 = " << num1 << endl;
//预留100000个空间
vector<int> v2;
v2.reserve(100000);
int num2 = 0; //统计开辟空间的次数
int* p2 = NULL;
for (int i = 0; i < 100000; i++)
{
v2.push_back(i + 1);
if (p2 != &v2[0])
{
p2 = &v2[0];
num2++;
}
}
cout << "num2 = " << num2 << endl;
}
int main(void)
{
test01();
return 0;
}
string容器容器:
striing头文件:
#include
string是Cpp风格的字符串,而string的本质是一个类。string和char*的区别在于: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初始化。
示例:
//string容器的4种构造函数
#include
#include
using namespace std;
void test01()
{
string s1; //默认构造函数
const char* str = "Hello World";
string s2(str);
cout << "s2 = " << s2 << endl;
string s3(s2);
cout << "s3 = " << s3 << endl;
string s4(10, 'c');
cout << "s4 = " << s4 << endl;
}
int main(void)
{
test01();
return 0;
}
string赋值操作,赋值的函数原型:
string& operator=(const char* s);//重载=操作符,char*类型字符串赋值给当前的字符串;string& operator=(const string &str);//重载=操作符,把字符串str赋值给当前的字符串;string& operator=(char c);//重载=操作符,把字符值赋值给当前的字符串;string& assign(const char* s);//把字符串s赋值给当前的字符串string& assign(const char* s, int n);//把字符串s的前n个字符赋值给当前的字符串;string& assign(const strng& str);//把字符串srt赋值给当前字符串;string& assign(int n, char c);//用n个字符c赋值给当前字符串。
string的赋值方式有很多,一般来说operator=的这种赋值方式最常用,示例:
//string容器的7种赋值操作
#include
#include
using namespace std;
void test01()
{
string str1;
str1 = "Hello World!";
cout << "str1 = " << str1 << endl;
string str2;
str2 = str1;
cout << "str2 = " << str2 << endl;
string str3;
str3 = 'c';
cout << "str3 = " << str3 << endl;
string str4;
str4.assign("Hello Cpp");
cout << "str4 = " << str4 << endl;
string str5;
str5.assign("Hello Cpp", 4);
cout << "str5 = " << str5 << endl;
string str6;
str6.assign(str5);
cout << "str6 = " << str6 << endl;
string str7;
str7.assign(10, 'c');
cout << "str7 = " << str7 << endl;
}
int main(void)
{
test01();
return 0;
}
string字符串拼接,函数原型:
string& operator+=(const char* s);//重载+=操作符;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& str);//同operator+=(const string& str);string& append(const string& str, int pos, int n);//字符串str从pos开始的n个字符连接到字符串结尾。
string的字符串拼接操作有很多,一般来说最常用的是重载operator+=,示例:
//string容器的7种拼接操作
#include
#include
using namespace std;
void test01()
{
string str1("Hello ");
str1 += "World!";
cout << "str1 = " << str1 << endl;
string str2 = str1;
str2 += 'A';
cout << "str2 = " << str2 << endl;
string str3 = str1;
str3 += str1;
cout << "str3 = " << str3 << endl;
string str4("Hello ");
str4.append("Cpp");
cout << "str4 = " << str4 << endl;
string str5("Hello ");
str5.append("Cpp", 2);
cout << "str5 = " << str5 << endl;
string str6("Cpp");
str6.append(str1);
cout << "str6 = " << str6 << endl;
string str7;
str7.append(str6, 1, 4);
cout << "str7 = " << str7 << endl;
}
int main(void)
{
test01();
return 0;
}
string查找和替换,函数原型:
int find(const string& str, int pos = 0) const;//查找str第一次出现的位置,从pos开始查找;int find(const char* s, int pos = 0) const;//查找s第一次出现的位置,从pos开始查找;int find(const char* s, int pos, int n) const;//从pos位置查找s的前n个字符第一次出现的位置;int find(const char c, int pos = 0) const;//查找字符c第一次出现的位置;int rfind(const string& str, int pos = npos) const;//查找str最后一次位置,从pos开始查找;int rfind(const char* s, int pos = npos) const;//查找s最后一次出现的位置,从pos开始查找;int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后一次出现的位置;int rfind(const char c, int pos = 0) const;//查找从pos开始n个字符为字符串str;string& replace(int pos, int n, const string& str);//替换从pos开始n个字符为字符串str;string& replace(int pos, int n, const char* s);//替换从pos开始的n个字符为字符串s
总结:注意,如果没有查找到所需的字符串或字符,则返回-1;find和rfind的区别在于find是从右往左查找,而rfind是从左往右查找,即rfind保存的是最后一次查找到的位置,而find保存的是第一次查找到的位置。replace在替换的时候,需要指定从什么位置开始替换,替换多少个字符,替换成什么字符串。
string字符串比较,函数原型:
int compare(const string& str) const;//与字符串str比较;int compare(const char* s) const;//与字符串s比较。
总结:注意,字符串的比较是对字符逐个按照ASCII码进行比较,=返回0,>返回1,<返回-1。字符串比较主要是用于判断两个字符串是否相等,判断谁打谁小的意义并不是很大。
string字符存取,函数原型:
char operator[](int n);//重载[]操作符,通过[]方式存取;char& at(int n);//通过at方式存取。
总结:可以通过上述两种方式来进行访问和修改,示例:
//string字符存取
#include
#include
using namespace std;
void test01()
{
string str("Hello World!");
//1.通过[]访问
for (int i = 0; i < str.size(); i++) //string.size()返回字符串的长度
{
cout << str[i] << " ";
}
cout << endl;
//2.通过at访问
for (int i = 0; i < str.size(); i++)
{
cout << str.at(i) << " ";
}
cout << endl;
//对字符串进行修改
str[0] = 'A';
cout << str << endl;
str.at(1) = 'B';
cout << str << endl;
}
int main(void)
{
test01();
return 0;
}
string插入和删除,函数原型:
string& insert(int pos, const char* s);//插入字符串s;string& insert(int pos, const string& str);//插入字符串str;string& insert(int pos, int n, char c);//在指定位置pos插入n个字符;string& erase(int pos, int n = npos);//删除从pos开始的n个字符。
string子串,函数原型:
string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串。
总结:子串常用于在字符串中截取一段字符,示例:
// string子串
#include
#include
using namespace std;
void test01() //使用操作,从邮箱中截取用户名
{
string str = "shenjiahao0610@163.com";
int pos = str.find('@');
cout << pos << endl;
string username = str.substr(0, pos);
cout << username << endl;
}
int main(void)
{
test01();
return 0;
}
deque容器容器:
deque头文件:
#include迭代器:
deque::iterator
deque容器是双端数组,可以从两端进行插入和删除;deque和vector的区别在于:
vector对于头部的插入删除效率低下,数据量越大,效率越低;deque相对而言,对头部的插入删除速度会比vector块;vector访问元素时的速度会比deque块,这和两者的内部实现有关。

deque的内部工作原理:deque内部有一个中控器,维护者每段缓冲区中的内容,缓冲区存放真实数据,中控器维护的时每个缓冲区的地址,使得使用deque时像一片连续的内存空间。deque的迭代器也是支持随机访问的。

deque构造函数,函数原型:
deque//默认构造函数;deq; deque(iterator beg, iterator end);//构造函数将[beg, end)区间的元素拷贝给本身;deque(n, elem);//构造函数将n个elem拷贝给本身;deque(const deque& deq);//拷贝构造函数。
示例:
//deque构造函数
#include
#include
using namespace std;
void printDeque(const deque<int>& deq)
{
//对于const修饰的函数原型,智能使用只读的迭代器const_iterator
for (deque<int>::const_iterator it = deq.begin(); it != deq.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//1.默认构造函数
deque<int> deq1;
for (int i = 0; i < 10; i++)
{
deq1.push_back(i + 1);
}
printDeque(deq1);
//2.以区间的方式
deque<int> deq2(deq1.begin(), deq1.end());
printDeque(deq2);
//3.n个elem的方式
deque<int> deq3(3, 10);
printDeque(deq3);
//4.拷贝构造的方式
deque<int> deq4(deq3);
printDeque(deq4);
}
int main(void)
{
test01();
return 0;
}
总结:deque容器和vector容器的构造方式几乎一样,灵活使用即可。
deque赋值操作,函数原型:
deque& operator=(const deque& deq);//重载=操作符;assign(beg, end);//将[beg, end)区间内的数据拷贝赋值给本身;assign(n, elem);//将n个elem拷贝赋值给本身。
示例:
//deque容器赋值操作
#include
#include
using namespace std;
void printDeque(const deque<int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
deque<int> deq1;
for (int i = 0; i < 10; i++)
{
deq1.push_back(i + 1);
}
printDeque(deq1);
deque<int> deq2;
deq2 = deq1;
printDeque(deq2);
deque<int> deq3;
deq3.assign(deq1.begin(), deq1.end());
printDeque(deq3);
deque<int> deq4;
deq4.assign(5, 10);
printDeque(deq4);
}
int main(void)
{
test01();
return 0;
}
deque大小操作,函数原型:
empty();//判断deque容器是否为空;size();//返回deque容器中元素的个数;resize(num);//重新指定deque容器的长度为num,若容器变长,则以默认值填充位置;若容器变短,则末尾超出容器长度的元素被删除。resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem填充新位置;若容器变短,则末尾超出容器长度的元素被删除。
示例:
//deque容器的大小操作
#include
#include
using namespace std;
void printDeque(const deque<int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void Isempty(const deque<int>& d)
{
if (d.empty())
{
cout << "deque is empty" << endl;
}
else
cout << "deque isn't empty" << endl;
}
void test01()
{
deque<int> deq1;
Isempty(deq1);
for (int i = 0; i < 10; i++)
{
deq1.push_back(i + 1);
}
Isempty(deq1);
printDeque(deq1);
cout << "deque has " << deq1.size() << " element" << endl;
deq1.resize(5);
printDeque(deq1);
cout << "deque has " << deq1.size() << " element" << endl;
deq1.resize(8, 2);
printDeque(deq1);
cout << "deque has " << deq1.size() << " element" << endl;
}
int main(void)
{
test01();
return 0;
}
deque插入和删除,函数原型:
push_back(elem);//在deque容器尾部插入一个数据;push_front(elem);//在deque容器头部插入一个数据;pop_back();//删除容器最后一个数据;pop_front();//删除容器第一个数据;insert(const_iterator pos, elem);//在pos位置插入一个elem元素的拷贝,返回新的数据的位置;insert(const_iterator pos, n, elem);//在pos位置插入n个elem元素,无返回值;insert(const_iterator pos, beg, end);//在pos位置插入[beg, end)区间的数据,无返回值;erase(const_iterator start, const_iterator end);//删除[start, end)区间的数据,返回下一个数据的位置;erase(const_iterator pos);//删除pos位置的数据,返回下一个数据的位置。
示例:
//deque容器的插入和删除
#include
#include
using namespace std;
void printDeque(const deque<int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
deque<int> deq1;
deq1.push_back(10);
deq1.push_front(5);
deq1.push_back(2);
printDeque(deq1);
deq1.pop_back();
deq1.pop_front();
printDeque(deq1);
//插入一个元素
deq1.insert(deq1.begin(), 3);
printDeque(deq1);
//插入n个elem
deq1.insert(deq1.end(), 2, 10);
printDeque(deq1);
//插入一个区间
deq1.insert(deq1.begin(), deq1.begin(), deq1.end());
printDeque(deq1);
//删除指定位置的元素
deque<int>::const_iterator iter = deq1.begin();
iter++;
deq1.erase(iter);
printDeque(deq1);
//删除指定区间内的元素
deq1.erase(iter, deq1.end());
printDeque(deq1);
}
int main(void)
{
test01();
return 0;
}
deque数据存取,函数原型:
operator[](int idx);//重载[]运算符,返回idx索引指向的元素;at(int idx);//返回索引idx所指向的元素;front();//返回容器中的第一个元素;back();//返回容器中最后一个元素。
deque排序操作,算法#include ,算法原型:
sort(const_iterator beg, const_iterator end);//对beg和end区间内的元素进行排序,默认从小到大排序。对于支持随机访问迭代器的容器,都可以利用sort算法来直接进行排序,vector容器也可以利用sort进行排序。
示例:
//deque容器的排序
#include
#include
#include
using namespace std;
void printDeque(const deque<int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
deque<int> deq;
deq.push_back(3);
deq.push_back(2);
deq.push_back(4);
deq.push_back(1);
deq.push_back(5);
printDeque(deq);
sort(deq.begin(), deq.end());
printDeque(deq); //排序后
}
int main(void)
{
test01();
return 0;
}
stack容器 stack是一种先进后出(First in first out, FIFO)的数据结构,又称为堆栈,它只有一个出口就是在它的栈顶,栈中只有栈顶的元素才可以被外界使用,因此栈是不允许有遍历行为的。

stack常用接口,构造函数:
stack//stack采用类模板实现,stack对象的默认构造函数;stk; stack(const stack& stk);//拷贝构造函数;
赋值操作:
stack& operator=(const stack& stk);//重载=操作符,执行赋值运算;
数据存取:
push(elem);//向栈顶添加元素;pop();//从栈顶移除第一个元素;top();//返回栈顶元素;
大小操作:
empty();//判断堆栈是否为空;size();//返回栈的大小。
示例:
//stack容器
#include
#include
using namespace std;
void test01()
{
stack<int> stk;
stk.push(10);
stk.push(20);
stk.push(30);
while (!stk.empty())
{
cout << "当前有 " << stk.size() << " 个元素"
<< "当前栈顶元素为 " << stk.top() << endl;
stk.pop();
}
cout << "当前栈中元素个数为 " << stk.size() << endl;
}
int main(void)
{
test01();
return 0;
}
queue容器 queue容器是以中国先进先出(First in first out,FIFO)的数据机构,又称为队列,它有两个出口

从队尾进入,从队头出去,因此只有队头和队尾能够被外界访问,队列是不允许有遍历行为的。
queue常用接口,构造函数:
queue//queue采用类模板实现,queue对象的默认构造函数;que; queue(const queue& que);//拷贝构造函数;
赋值操作:
queue& operator=(const queue& que);//重载=赋值操作符;
数据存取:
push(elem);//往队尾添加元素;pop();//移除队头的第一个元素;back();//返回队尾最后一个元素;front();//返回第一个元素。
大小操作:
empty();//判断堆栈是否为空;size();//返回栈的大小。
示例:
//queue容器
#include
#include
#include
using namespace std;
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
this->m_Age = age;
this->m_Name = name;
}
void ShowInfo()
{
cout << "姓名:" << this->m_Name
<< "\t年龄:" << this->m_Age << endl;
}
};
void test01()
{
Person p1("Tom", 22);
Person p2("Alice", 21);
Person p3("Baby", 22);
Person p4("Hello", 20);
Person p5("World!", 22);
queue<Person> que;
que.push(p1);
que.push(p2);
que.push(p3);
que.push(p4);
que.push(p5);
while (!que.empty())
{
cout << "当前队列元素个数为:" << que.size() << " 当前的队头元素:\t";
que.front().ShowInfo();
que.pop();
}
}
int main(void)
{
test01();
return 0;
}
list容器 list容器是将数据进行链式存储,是一种链表的数据结构,链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。链表的组成:链表由一系列结点组成;结点的组成:结点包含一个存储数据元素的数据域,还有一个指向下一个结点地址的指针域。在STL中实现的链表是一个双向循环链表。

由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,则链表的迭代器属于双向迭代器。list的优点如下:
- 采用动态内存分配,不会造成内存浪费和溢出;
- 链表执行插入和删除操作非常方便,只需要修改指针即可,不需要移动大量元素。
list的缺点如下:
- 链表灵活,但是空间(指针域)和时间(遍历)额外消耗较大。
list容器有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这是在vector容器中不成立的。
总结:STL中list和vector是最常用的两个容器,它们各有优缺点。
list构造函数,函数原型:
list//list采用模板类实现,对象的默认构造函数;lst; list(iterator beg, iterator end);//构造函数将[beg, end)区间中的元素拷贝给本身;list(n, elem);//拷贝构造函数将n个elem拷贝给本身;list(const list& lst);//拷贝构造函数。
示例:
//list容器的构造函数
#include
#include
using namespace std;
void printList(const list<int>& lst)
{
for (list<int>::const_iterator it = lst.begin(); it != lst.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
list<int> lst1; //1.默认构造函数
for (int i = 0; i < 10; i++)
{
lst1.push_back(i + 1);
}
printList(lst1);
lst1.pop_back();
list<int> lst2(lst1); //2.拷贝构造函数
printList(lst2);
list <int>::iterator iter = lst2.begin();
list<int> lst3(++iter, lst2.end()); //3.拷贝构造函数
printList(lst3);
list<int> lst4(4, 5); //4.n个elem构造函数
printList(lst4);
}
int main(void)
{
test01();
return 0;
}
list赋值和交换,函数原型:
assign(iterator beg, iterator end);//将[beg, end)区间内的数据拷贝赋值给本身;assign(n, elem);//将n个elem拷贝赋值给本身;list& operator=(const list& lst);//重载=运算符;swap(lst);//将lst与本身的元素互换,与vector容器的互换操作类似。
示例:
//list赋值和交换
#include
#include
using namespace std;
void printList(const list<int>& L)
{
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
list<int> lst1;
for (int i = 0; i < 10; i++)
{
lst1.push_back(10 * (i + 1));
}
printList(lst1);
list<int>::iterator It = lst1.begin();
list<int> lst2; //1.重载operator=赋值
lst2 = lst1;
list<int> lst3;
lst3.assign(lst1.begin(), lst1.end()); //2.assign的方式
printList(lst3);
printList(lst2);
lst1.assign(5, 1); //3.将n个elem赋值给lst1
printList(lst1);
lst1.swap(lst2); //4.swap交换
printList(lst2);
}
int main(void)
{
test01();
return 0;
}
list大小操作,函数原型:
size();//返回容器中元素的个数;empty();//判断容器是否为空;resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新的位置;若容器变短,则末尾超出容器长度的元素被删除;resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem填充新的位置;若容器变短,则末尾超出容器长度的元素被删除。
示例:
//list容器的大小操作
#include
#include
using namespace std;
void printList(const list<int>& L)
{
cout << "当前链表中的元素个数为:" << L.size() << endl;
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
list<int> lst;
for (int i = 0; i < 5; i++)
{
lst.push_back(10 * (i + 1));
}
printList(lst);
if (!lst.empty())
{
lst.resize(6);
printList(lst);
}
lst.resize(10, 0);
printList(lst);
}
int main(void)
{
test01();
return 0;
}
list插入和删除,函数原型:
push_back(elem);//在容器尾部加入一个元素;pop_back();//删除容器中最后一个元素;push_front(elem);//在容器的开头插入一个元素;pop_front();//删除容器中第一个元素;insert(const_iterator pos, elem);//在pos位置插入elem元素的拷贝,返回新的数据的位置;insert(const_iterator pos, n, elem);//在pos位置插入n个elem数据,无返回值;insert(const_iterator pos, iterator beg, iterator end);//在pos位置插入[beg, end)区间的数据,无返回值;clear();//移除容器中所有的数据;erase(iterator beg, iterator end);//删除[beg, end)区间中的数据,返回下一个数据的位置;erase(iterator pos);//删除pos位置的数据,返回下一个数据的位置;remove(elem);//删除容器中 所有与elem值匹配的元素。
示例:
//list容器的插入和删除
#include
#include
using namespace std;
void printList(const list<int>& L)
{
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
list<int> lst;
//1.插入
lst.push_back(10);
lst.push_front(20);
printList(lst);
//2.删除
lst.pop_back();
lst.pop_front();
//3.插入
lst.insert(lst.begin(), 5);
printList(lst);
lst.insert(lst.end(), 5, 10);
printList(lst);
lst.insert(lst.end(), lst.begin(), lst.end());
printList(lst);
//4.清除
lst.erase(lst.begin());
printList(lst);
//lst.erase(lst.begin(), lst.end()); //与lst.clear()等价
//5.删除指定元素
lst.remove(10);
printList(lst);
}
int main(void)
{
test01();
return 0;
}
list数据存取,list容器并没有重载[],也不能使用at来进行访问,原因是因为,list是链表并不是一段连续的线性空间,list的迭代器也不支持随机访问,函数原型:
front();//返回第一个元素;back();//返回最后一个元素。
示例:
//list容器的存取
#include
#include
using namespace std;
void test01()
{
list <int> lst;
lst.push_back(10);
lst.push_back(20);
lst.push_back(30);
cout << "第一个元素: " << lst.front() << endl;
cout << "最后一个元素: " << lst.back() << endl;
}
int main(void)
{
test01();
return 0;
}
list反转和排序,函数原型:
reverse();//反转链表;sort();//链表排序。
示例:
//list反转和排序
#include
#include
#include
using namespace std;
void printList(const list<int>& L)
{
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
bool myCompare(int v1, int v2) //回调函数,自定义比较方式,让排序为降序
{
return v1 > v2;
}
void test01()
{
list<int> lst;
lst.push_back(10);
lst.push_back(40);
lst.push_back(30);
lst.push_back(20);
lst.push_back(50);
printList(lst);
lst.reverse(); //倒序
printList(lst);
//注:所有不支持随即迭代器的容器,不能够使用STL标准算法algorithm
//例如这样做是错的
//sort(lst);
lst.sort(); //应该调用lst自己的排序方法,默认升序
printList(lst);
lst.sort(myCompare); //更改为降序
printList(lst);
}
int main(void)
{
test01();
return 0;
}
排序案例,对自定义Person类型进行排序,首先按照年龄来进行排序,如果年龄相同则按照身高来进行排序。
//list排序案例
#include
#include
#include
using namespace std;
//按照年龄来进行排序,如果年龄相同则按照身高来进行排序
class Person
{
public:
int m_Age;
double m_Height;
string m_Name;
Person(string name, int age, double height)
{
this->m_Age = age;
this->m_Height = height;
this->m_Name = name;
}
void ShowInfo() const //常函数
{
cout << "姓名 " << this->m_Name
<< "\t年龄 " << this->m_Age
<< "\t身高 " << this->m_Height << endl;
}
};
void printList(const list<Person>& L) //参数是一个常对象
{
for (list<Person>::const_iterator it = L.begin(); it != L.end(); it++)
{
it->ShowInfo();
}
}
//bool myCompare(Person* p1, Person* p2) //排序回调函数,参数类型不能为指针,否则会报错
//{
// if (p1->m_Age == p2->m_Age)
// {
// return p1->m_Height < p2->m_Height;
// }
// else
// return p1->m_Age < p2->m_Age;
//}
bool myCompare(Person& p1, Person& p2) //排序回调函数
{
if (p1.m_Age == p2.m_Age)
{
return p1.m_Height < p2.m_Height;
}
else
return p1.m_Age < p2.m_Age;
}
void test01()
{
Person p1("Tom", 22, 183.0);
Person p2("Alice", 22, 180.5);
Person p3("Eric", 20, 190.1);
Person p4("Motty", 18, 180.0);
Person p5("Rick", 22, 184);
Person p6("Jane", 23, 180.2);
list<Person> lst;
lst.push_back(p1);
lst.push_back(p2);
lst.push_back(p3);
lst.push_back(p4);
lst.push_back(p5);
lst.push_back(p6);
printList(lst);
lst.sort(myCompare);
cout << "------------------------" << endl;
printList(lst);
}
int main(void)
{
test01();
return 0;
}
set/multiset容器 set的基本概念,set被称为集合,其中所有的元素都会在插入时被自动进行排序(针对内置数据类型),本质上set/multiset属于关联容器,底层结构是用二叉树进行实现的。两者区别在于:
set中不允许有重复的元素;multiset中允许有重复的元素。
set的构造和赋值,构造函数,函数原型:
set//默认构造函数;st; set(const set &st);//拷贝构造函数。
赋值操作,函数原型:
set& operator=(const set& st);//重载=运算符,进行拷贝操作。
示例:
//set构造和拷贝
#include
#include
using namespace std;
void printSet(const set<int>& S)
{
for (set<int>::const_iterator it = S.begin(); it != S.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//1.默认构造函数
set<int> st1;
//set容器在插入的时候,只有insert方法
st1.insert(10);
st1.insert(30);
st1.insert(20);
st1.insert(40);
st1.insert(40); //set容器不支持插入重复的值,不会报错,但是插入不成功
printSet(st1); //set容器会自动排序
//2.拷贝构造函数
set<int> st2(st1);
printSet(st2);
//3.赋值运算
set<int> st3;
st3 = st1;
printSet(st3);
}
int main(void)
{
test01();
return 0;
}
总结:
set容器插入数据时用insert;set容器插入数据时会对数据进行自动排序。
set大小操作和交换,函数原型:
size();//返回容器中元素的数目;empty();//判断容器是否为空;swap();//交换两个集合容器,类似于vector和list。
示例:
//set容器大小和交换操作
#include
#include
using namespace std;
void printSet(const set<int>& S)
{
if (S.empty())
{
cout << "集合为空" << endl;
return;
}
cout << "集合的元素个数:" << S.size() << endl;
for (set<int>::const_iterator it = S.begin(); it != S.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int> st1;
st1.insert(10);
st1.insert(20);
st1.insert(30);
st1.insert(40);
printSet(st1);
set<int> st2;
st2.swap(st1); //交换集合中元素
printSet(st1);
printSet(st2);
}
int main(void)
{
test01();
return 0;
}
set插入和删除,函数原型:
insert(elem);//在容器中插入元素,只有这种插入方式;clear();//清除容器中所有元素;erase(iterator pos);//删除pos迭代器所指向的元素,返回指向下一个元素的迭代器;erase(iterator beg, iterator end);//删除区间[beg, end)的所有元素,返回指向下一个元素的迭代器;erase(elem);//删除容器中值为elem的元素。
示例:
//set容器插入和删除操作
#include
#include
using namespace std;
void test01()
{
set<int> st1;
st1.insert(10);
st1.insert(20);
st1.insert(30);
st1.erase(st1.begin());
st1.erase(20); //删除st1中的
st1.erase(st1.begin(), st1.end());//st1.clear();
}
int main(void)
{
test01();
return 0;
}
set查找和统计,函数原型:
find(key);//查找key是否存在,若存在,返回指向该键元素的迭代器;若不存在则返回set.end();;count(key);//统计key的元素个数,返回的结果只有0或1。
示例:
//set的查找和统计
#include
#include
using namespace std;
void printSet(const set<int>& S)
{
for (set<int>::const_iterator it = S.begin(); it != S.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void findElem(const set<int>& S, int elem)
{
set<int>::iterator It = S.find(elem);
if (It != S.end())
{
cout << "查找的元素为: " << *It << endl;
}
else
cout << "查找失败" << endl;
}
void coutElem(const set<int>& S, int elem)
{
cout << "统计元素 " << elem << " 的个数为 " << S.count(elem) << endl;
}
void test01()
{
set<int> st1;
for (int i = 0; i < 10; i++)
{
st1.insert(i + 1);
}
printSet(st1);
findElem(st1, 5);
findElem(st1, 11);
coutElem(st1, 5);
coutElem(st1, 11);
}
int main(void)
{
test01();
return 0;
}
set和multiset的区别:
set不可以插入重复数据,而multiset可以;set插入数据的同时会返回插入结果,返回值的类型是一个对组,表示插入是否成功;multiset不会检测数据,因此可以插入重复数据。
示例:
//set和multiset的区别
#include
#include
using namespace std;
void test01()
{
set<int> st; //不允许插入重复的数据
pair<set<int>::iterator, bool> ret = st.insert(10); //set用对组接受insert的返回值
if (ret.second == true)
cout << "插入成功" << endl;
else
cout << "插入失败" << endl;
ret = st.insert(10); //不能重复插入数据
if (ret.second == true)
cout << "插入成功" << endl;
else
cout << "插入失败" << endl;
multiset<int> mset;
mset.insert(10); //multiset用iterator接受insert的返回值
mset.insert(10);
for (multiset<int>::iterator it = mset.begin(); it != mset.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main(void)
{
test01();
return 0;
}
pair对组的创建,对组用于描述成对出现的数据,利用对组可以返回两个数据,函数原型:
pair//有参构造函数;p(value1, value2); pair//利用p = make_pair(value1, value2); make_pair函数进行构造,可以单独使用make_pair来生成对组而无需指定模板参数,make_pair会自动生成模板参数。
pair对组的访问,
.first//访问对组中第一个元素;.second//访问对组中第二个元素。
示例:
//pair的使用
#include
using namespace std;
void test01()
{
//1.有参构造创建对组
pair<string, int> p1("Tom", 22);
cout << "姓名:\t" << p1.first << "\t年龄:\t" << p1.second << endl;
//2.make_pair创建对组
pair<string, int> p2 = make_pair("Alice", 21);
cout << "姓名:\t" << p2.first << "\t年龄:\t" << p2.second << endl;
}
int main(void)
{
test01();
return 0;
}
set容器排序规则更改,set容器默认的排序规则是从小到大,我们利用仿函数可以改变排序的规则。
map/multimap容器 map容器中所有的元素都是pair;pair中第一个元素key(键值),起到索引的作用,第二个元素value(实值);所有的元素都会根据元素的键值进行自动排序。(类似于python中的dict)。map/multimap的本质都属于关联式容器,底层结构是用二叉树进行实现的。使用map/multimap的优点是可以根据key值快速找到value值。map/multimap的区别在于:
map不允许容器中有重复的key值元素;multimap允许容器中有重复的key值元素。
map构造和赋值,函数原型:
map//mp; map默认构造函数,其中T1是key的类型,T2是value的类型;map(const map& mp);//map拷贝构造函数;map& operator=(const map& mp);//重载=运算符。
示例:
//map容器的构造和赋值
#include
#include
#include
using namespace std;
void printMap(const map<string, int>& M)
{
for (map<string, int>::const_iterator it = M.begin(); it != M.end(); it++)
{
cout << "Key: " << it->first
<< "\tValue: " << it->second << endl;
}
cout << "-----------------------------" << endl;
}
void test01()
{
map<string, int> mp; //1.默认构造函数
mp.insert(pair<string, int>("a", 10));
mp.insert(pair<string, int>("b", 20));
mp.insert(pair<string, int>("Hello", 50));
mp.insert(pair<string, int>("World!", 100));
printMap(mp);
//2.拷贝构造函数
map<string, int> mp2(mp);
printMap(mp2);
//3.赋值操作
map<string, int> mp3 = mp;
printMap(mp3);
}
int main(void)
{
test01();
return 0;
}
map大小和交换操作,函数原型:
.size();//返回容器中元素的数目;.empty();//判断容器是否为空;.swap(mp);//交换两个map容器。
示例:
//map容器的大小操作
#include
#include
using namespace std;
void printMap(const map<int, int> M)
{
if (M.empty())
cout << "map容器为空" << endl; //1.大小操作
else
{
cout << "map容器不为空" << endl;
cout << "元素个数为:" << M.size() << endl;
}
for (map<int, int>::const_iterator it = M.begin(); it != M.end(); it++)
{
cout << "key:" << it->first
<< "\tvalue:" << it->second << endl;
}
cout << "----------------------" << endl;
}
void test01()
{
map<int, int> mp;
mp.insert(pair<int, int>(1, 10));
mp.insert(pair<int, int>(2, 20));
mp.insert(pair<int, int>(3, 30));
printMap(mp);
map<int, int> mp2;
mp2.insert(pair<int, int>(5, 10));
mp.swap(mp2); //2.交换操作
printMap(mp);
printMap(mp2);
}
int main(void)
{
test01();
return 0;
}
map插入和删除,函数原型:
insert(elem);//在容器中插入元素;clear();//清除所有元素;erase(iterator pos);//删除pos迭代器所指向的元素,返回下一个元素的位置的迭代器;erase(iterator beg, iterator end);//删除区间[beg, end)的所有元素,返回下一个元素的迭代器;erase(key);//删除容器中值为key的元素
示例:
//map插入和删除
#include
#include
#include
using namespace std;
void printMap(const map<string, int>& M)
{
if (M.empty())
cout << "map为空" << endl;
else
{
for (map<string, int>::const_iterator it = M.begin(); it != M.end(); it++)
{
cout << "Key:" << it->first
<< "\tValue:" << it->second << endl;
}
cout << "------------------------------" << endl;
}
}
void test01()
{
map<string, int> mp;
//插入操作
//1.pair插入方式
mp.insert(pair<string, int>("Alice", 22));
//2.make_pair插入方式
mp.insert(make_pair("Eirc", 20));
//3.value_type
mp.insert(map<string, int>::value_type("Jerry", 23));
//4.重载[]的方式
mp["Jane"] = 21;
printMap(mp);
//删除操作
//1.erase(pos)
map<string, int>::iterator iter = mp.begin();
mp.erase(++iter);
printMap(mp);
//2.erase(elem)
mp.erase("Alice");
printMap(mp);
//3.erase(beg, end) //3.clear()
mp.erase(mp.begin(), mp.end());
printMap(mp);
}
int main(void)
{
test01();
return 0;
}
map的查找和统计,函数原型:
operator[](T1 key);//重载[]运算符,返回key所指向的value;find(key);//查找key是否存在,若存在则返回该键元素的迭代器;若不存在则返回set.end();count(key);//统计key的元素个数,因为map不允许存在重复的键key,因此统计的结果只能为0或1;multimap允许存在重复的键,因此统计的结果可能大于1。
示例:
//map查找和统计
#include
#include
using namespace std;
void printMap(const map<string, int>& M)
{
for (map<string, int>::const_iterator it = M.begin(); it != M.end(); it++)
{
cout << "Key:" << it->first
<< "\tValue:" << it->second << endl;
}
cout << "------------------------------" << endl;
}
void test01()
{
map<string, int> mp;
mp.insert(make_pair("Tom", 22));
mp.insert(pair<string, int>("Alice", 21));
mp.insert(map<string, int>::value_type("Jack", 23));
mp["Eric"] = 24;
printMap(mp);
//1.find查找
map<string, int>::iterator pos = mp.find("Jack");
if (pos != mp.end())
cout << pos->first << " " << pos->second << endl;
else
cout << "未找到该元素" << endl;
//2.count统计
cout << mp.count("Hello") << endl;
cout << mp.count("Tom") << endl;
}
int main(void)
{
test01();
return 0;
}