C++提高编程(1)
C++提高编程
- 1.模板
- 1.1模板的概念
- 1.2函数模板
- 1.2.1函数模板语法
- 1.2.2函数模板注意事项
- 1.2.3函数模板案例
- 1.2.4普通函数与函数模板的区别
- 1.2.5普通函数与函数模板的调用规则
- 1.2.6模板的局限性
- 1.3类模板
- 1.3.1类模板语法
- 1.3.2类模板和函数模板区别
- 1.3.3类模板中成员函数创建时机
- 1.3.4类模板对象做函数参数
- 1.3.5类模板与继承
- 1.3.6类模板成员函数类外实现
- 1.3.7类模板分文件编写
- 1.3.8类模板与友元
- 1.3.9类模板案例
- 2.STL初始
- 2.1STL的诞生
- 2.2STL的基本概念
- 2.3STL六大组件
- 2.4STL容器、算法、迭代器
- 2.4.1容器
- 2.4.2算法
- 2.4.3迭代器
- 2.5容器算法迭代器初识
- 2.5.1 vector存放内置数据类型
- 2.5.2vector存放自定义数据类型
- 2.5.3容器嵌套
- 3.STL-常用容器
- 3.1string容器
- 3.1.1string基本概念
- 3.1.2string构造函数
- 3.1.3string赋值操作
- 3.1.4string字符拼接
- 3.1.5string查找和替换
- 3.1.6string字符串比较
- 3.1.7string字符存取
- 3.1.8string插入和删除
- 3.1.9string子串
- 3.2vector容器
- 3.2.1vector基本概念
- 1.功能
- 2.vector与普通数组区别
- 3.动态扩展
- 3.2.2vector构造函数
- 3.2.3vector赋值操作
- 3.2.4vector容量和大小
- 3.2.5vector插入和删除
- 3.2.6vector数据存取
- 3.2.7vector互换容器
- 3.2.8vector预留空间
- 3.3deque容器
- 3.3.1deque容器基本概念
- 3.3.2deque构造函数
- 3.3.3deque赋值操作
- 3.3.4deque大小操作
- 3.3.5deque插入删除
- 3.3.6deque数据存取
- 3.3.7deque排序
- 3.4案例
- 3.5stack容器
- 3.5.1stack基本概念
- 3.5.3stack常用接口
- 1.构造函数
- 2.赋值操作
- 3.数据存取
- 4.大小操作
- 3.6queue容器
- 3.6.1queue基本概念
- 3.6.2queue常用接口
- 1.构造函数
- 2.赋值操作
- 3.数据存取
- 4.大小操作
- 3.7list容器
- 3.7.1list基本概念
- 3.7.2list构造函数
- 3.7.3list赋值和交换
- 3.7.4list大小操作
- 3.7.5list插入和删除
- 3.7.6list数据存取
- 3.7.7list反转和排序
- 3.8set/multiset容器
- 3.8.1set基本概念
- 1.简介
- 2.本质
- 3.set和multiset区别
- 3.8.2set构造和赋值
- 3.8.3set大小和交换
- 3.8.4set插入和删除
- 3.8.5set查找和统计
- 3.8.6set和multiset区别
- 3.8.7pair对组创建
- 3.8.8set容器排序
- 3.9map/multimap容器
- 3.9.1map基本概念
- 1.简介
- 2.本质
- 3.优点
- 4.map和multimap区别
- 3.9.2map构造和赋值
- 3.9.3map大小和交换
- 3.9.4插入和删除
- 3.9.5map查找和统计
- 3.9.6map容器排序
1.模板
1.1模板的概念
- 模板就是建立通用的摸具,大大提高复用性
- 模板的特点
- 模板不可以直接使用,它只是一个框架
- 模板的通用并不是万能的
1.2函数模板
- C++中另一种编程思想称为泛型编程,主要利用的技术就是模板
- C++提供两种模板机制:函数模板和类模板
1.2.1函数模板语法
-
函数模板作用
- 建立一个通用函数,其函数返回值类型和形参类型可以不具体定制,用一个虚拟的类型来代表
-
语法
template<typename T>
函数声明或定义
- 解释
- template — 声明创建模板
- typename — 表面起后面的符号是一种数据类型,可以用class替代
- T — 通用的数据类型加粗样式,名称可以替换,通常为大写字母
#include<iostream>
using namespace std;//函数模板
template<typename T> //声明一个模板,告诉编译器后面代码紧跟的T不要报错,T是一个通用类型
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test1()
{// 两种方式使用函数模板// 1.自动类型推导int a = 20;int b = 40;mySwap(a, b);cout << "a = " << a << endl; // 40cout << "b = " << b << endl; // 20// 2.显示指定类型mySwap<int>(a, b);cout << "a = " << a << endl; // 20cout << "b = " << b << endl; // 40
}int main()
{test1();system("pause");return 0;
}
1.2.2函数模板注意事项
- 注意事项
- 自动类型推导,必须推导出一致的数据类型T,才可以使用
- 模板必须要确定出T的数据类型,才可以使用
#include<iostream>
using namespace std;template<typename T>
void mySwap(T &a,T &b)
{T temp = a;a = b;b = temp;
}//模板必须要确定出T的数据类型,才可以使用
template<typename T>
void func()
{cout << "func的调用" << endl;
}void test1() {int a = 10;int b = 30;char c = 'a';mySwap(a, b);// mySwap(a, c); // error,推导不出不一致的T类型cout << "a = " << a << endl;cout << "b = " << b << endl;
}
void test2()
{func<int>();
}int main()
{//test1();test2();system("pause");return 0;
}
1.2.3函数模板案例
#include<iostream>
using namespace std;template<typename T>
void mySwap(T &a,T &b)
{T temp = a;a = b;b = temp;
}
template<typename T>
void arrSort(T arr[],int len)
{for (int i = 0;i < len;i++) {int max = i;for (int j = i + 1;j < len;j++) {if (arr[max] < arr[j]) {max = j;}}if(max != i){mySwap(arr[max], arr[i]);}}
}
template<typename T>
void showArr(T arr[], int len)
{for (int i = 0;i < len;i++) {cout << arr[i] << " ";}cout << endl;
}
void test1()
{char arr[] = "abefcej";arrSort<char>(arr, sizeof(arr) / sizeof(char));showArr(arr, sizeof(arr) / sizeof(char));
}
int main()
{test1();system("pause");return 0;
}
1.2.4普通函数与函数模板的区别
- 区别
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
#include<iostream>
using namespace std;//函数模板
template<typename T>
T myAdd2(T a, T b)
{return a + b;
}// 普通函数
int myAdd(int a, int b) {return a + b;
}
void test1() {int a = 12;int b = 13;char c = 's';cout << myAdd(a, b) << endl;// 25// 隐藏类型转化cout << myAdd(a, c) << endl;// 127cout << myAdd2(a, b) << endl;// 25// 自动类型推导 -- 不能自动转换(隐式转换)//cout << myAdd2(a, c) << endl;// error//显示指定类型cout << myAdd2<int>(a, c) << endl;// 127
}int main() {test1();system("pause");return 0;
}
总结: 建议使用显示指定类型的方式,调用函数模板,自己可以确定通用类型
1.2.5普通函数与函数模板的调用规则
- 调用规则
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
#include<iostream>
using namespace std;void myPrint(int a,int b)
{cout << "调用普通函数" << endl;
}template <typename T>
void myPrint(T a, T b)
{cout << "调用函数模板" << endl;
}template <typename T>
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, 20);//函数模板产生更好的匹配,优先调用函数模板char a1 = 'a';char b1 = 'b';myPrint(a1, b1);// 调用函数模板
}int main()
{test1();system("pause");return 0;
}
1.2.6模板的局限性
- 局限性
- 模板的通用性并不是万能的
- 利用具体化的模板,可以解决自定义类型的通用化
#include<iostream>
using namespace std;
#include<string>
class Person
{
public:Person(string name,int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};template<typename T>
bool myCompare(T& a, T& b)
{if (a == b) {return true;}else {return false;}
}
// 利用具体化Person的版本实现代码,具体优先调用
template<> bool myCompare(Person& p1, Person& p2)
{if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age) {return true;}else {return false;}
}void test1()
{int a = 10;int b = 20;bool res = myCompare(a, b);if (res) {cout << "a==b" << endl;}else {cout << "a != b" << endl;}
}
void test2()
{Person person1("Jack", 20);Person person2("Jack", 20);bool res = myCompare(person1, person2);if (res) {cout << "person1 == person2" << endl;}else {cout << "person1 != person2" << endl;}
}
int main()
{//test1();test2();system("pause");return 0;
}
1.3类模板
1.3.1类模板语法
-
类模板的作用
- 建立一个通用类,类中的成员 数据类型 可以不具体规定,用一个虚拟的类型来代表
-
语法
template<class T> 类
- template — 声明创建模板
- class — 表明其后免得符号是一种数据类型,可以用 typename 代替
- T — 通用的数据类型,名称可以替换,通常为大写字幕
#include<iostream>
using namespace std;
#include<string>//类模板
template<class NameType,class AgeType>
class Person
{
public:Person(NameType name,AgeType age) {this->m_Name = name;this->m_Age = age;}void showPerson() {cout << "name:" << this->m_Name << ",age:" << this->m_Age << endl;}NameType m_Name;AgeType m_Age;};
void test1()
{Person<string, int> p1("类模板", 20);p1.showPerson();
}int main()
{test1();system("pause");return 0;
}
1.3.2类模板和函数模板区别
- 类模板和函数模板的区别
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
#include<iostream>
using namespace std;//类模板和函数模板的区别//类模板在参数列表中可以有默认参数 AgeType = int
template<class NameType,class AgeType = int>
class Person
{
public:Person(NameType name,AgeType age){this->m_Name = name;this->m_Age = age;}void showPerson(){cout << "name:" << this->m_Name << ",age:" << this->m_Age << endl;}NameType m_Name;AgeType m_Age;
};void test1()
{// 类模板没有自动化类型推导使用方式//Person p("数字化", 10);Person<string, int> p("数字化", 10);p.showPerson();
}
void test2()
{Person<string> p("智能化", 10);p.showPerson();
}
int main()
{//test1();test2();system("pause");return 0;
}
1.3.3类模板中成员函数创建时机
- 类模板中成员函数和普通类中成员函数创建时机是有区别的
- 普通类中成员函数一开始就可以创建
- 类模板中成员函数在调用时才创建
#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 fun1() {obj.showPerson1();}void fun2() {obj.showPerson2();}
};void test1() {MyClass<Person1> m;m.fun1();// 类模板中成员函数在调用时创建// m.fun2();
}
int main() {system("pause");return 0;
}
1.3.4类模板对象做函数参数
- 类模板实例化出的对象,向函数传参的方式
- 指定传入的类型 — 直接显示对象的数据类型 (推荐)
- 参数模板化 — 将对象中的参数变为模板进行传递
- 整个类模板化 — 将这个对象类型 模板化 进行传递
#include<iostream>
using namespace std;
template<class T,class K>
class Person
{
public:Person(T name, K age) {this->m_Name = name;this->m_Age = age;}void showPerson() {cout << "name:" << this->m_Name << ",age:" << this->m_Age << endl;}T m_Name;K m_Age;};
// 1.指定传入类型(推荐)
void printPerson1(Person<string, int> &p)
{p.showPerson();
}void test1() {Person<string, int> p("智能化", 120);printPerson1(p);
}// 2.参数模板化
template<class T,class K>
void printPerson2(Person<T, K>& p) {p.showPerson();//查看推导的类型cout << "T的类型为:" << typeid(T).name() << endl; //class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >cout << "K的类型为:" << typeid(K).name() << endl; //int
}void test2() {Person<string, int> p("数字化", 10);printPerson2(p);
}// 3.整个类模板化
template<class T>
void printPerson3(T &p) {p.showPerson();cout << "T的数据类型: " << typeid(T).name() << endl;
}
void test3() {Person<string, int> p("自动化", 30);printPerson3(p);
}int main() {//test1();//test2();test3();system("pause");return 0;
}
1.3.5类模板与继承
- 类模板碰到继承时,需要注意一下几点
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中的T类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中的T的类型,子类也需要变成类模板
#include<iostream>
using namespace std;
#include<string>template<class T,class K>
class Person
{
public:Person(T name, K age);/*{this->m_Name = name;this->m_Age = age;}*/void showPerson();/*{cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;}*/T m_Name;K m_Age;};//构造函数的类外实现
template<class T,class K>
Person<T,K>::Person(T name, K age)
{this->m_Name = name;this->m_Age = age;
}
// 成员函数的类外实现
template<class T,class K>
void Person<T, K>::showPerson()
{cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
}void test1()
{Person<string,int> p("智能组装", 8);p.showPerson();
}
int main() {test1();system("pause");return 0;
}
1.3.6类模板成员函数类外实现
#include<iostream>
using namespace std;
#include<string>template<class T,class K>
class Person
{
public:Person(T name, K age);/*{this->m_Name = name;this->m_Age = age;}*/void showPerson();/*{cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;}*/T m_Name;K m_Age;};//构造函数的类外实现
template<class T,class K>
Person<T,K>::Person(T name, K age)
{this->m_Name = name;this->m_Age = age;
}
// 成员函数的类外实现
template<class T,class K>
void Person<T, K>::showPerson()
{cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl;
}void test1()
{Person<string,int> p("智能组装", 8);p.showPerson();
}
int main() {test1();system("pause");return 0;
}
1.3.7类模板分文件编写
-
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
-
解决方式:
- 直接包含.cpp源文件
- 头文件 person.h
#pragma once #include<iostream> using namespace std; #include<string> template<class T,class K> class Person { public:Person(T name,K age);void showPerson();T m_Name;K m_Age;};
-
- 源文件 person.cpp
#include "person.h"template<class T, class K> Person<T, K>::Person(T name, K age) {this->m_Name = name;this->m_Age = age; }template<class T, class K> void Person<T, K>::showPerson() {cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl; }
-
- 源文件
#include<iostream> using namespace std; #include<string>// 1.第一种解决方式,直接包含 源文件 //#include "person.cpp"// 2.第二种解决方式,将.h和.cpp中的内容写到一起,将后缀名改为.hpp文件 #include"person.hpp"//template<class T,class K> //class Person //{ //public: // Person(T name, K age); // void showPerson(); // T m_Name; // K m_Age; //}; // //template<class T,class K> //Person<T, K>::Person(T name, K age) //{ // this->m_Name = name; // this->m_Age = age; //} // //template<class T,class K> //void Person<T, K>::showPerson() //{ // cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl; //}void test1() {Person<string,int> person("数据化", 20);person.showPerson(); }int main() {test1();system("pause");return 0; }
- 将声明和实现写在同一个文件中,并更改后缀名.hpp,hpp是约定的名称,并不是强制
- 头文件 person.hpp
#pragma once #include<iostream> using namespace std; #include<string> template<class T, class K> class Person { public:Person(T name, K age);void showPerson();T m_Name;K m_Age;};template<class T, class K> Person<T, K>::Person(T name, K age) {this->m_Name = name;this->m_Age = age; }template<class T, class K> void Person<T, K>::showPerson() {cout << "姓名: " << this->m_Name << ",年龄:" << this->m_Age << endl; }
- 引入方式同上
- 直接包含.cpp源文件
1.3.8类模板与友元
- 全局函数类内实现 — 直接在类内声明友元即可
- 全局函数类外实现 — 需要提前让编译器知道全局函数的存在
#include<iostream>
#include<string>
using namespace std;// 声明Person类
template<class T, class K>
class Person;//类外实现template<class T, class K>
void printPerson2(Person<T, K> &p)
{cout << "-----------全局函数的类外实现-----------" << endl;cout << "姓名: " << p.m_Name << ",年龄: " << p.m_Age << endl;
}//类模板
template<class T,class K>
class Person
{// 全局函数 类内实现friend void printPerson(Person<T, K> p){cout << "姓名: " << p.m_Name << ",年龄: " << p.m_Age << endl;}//全局函数 类外实现// 加空模板参数列表// 全局函数 是类外实现,需要让编译器提前知道这个函数的存在friend void printPerson2<>(Person<T, K> &p);public:Person(T name, K age){this->m_Name = name;this->m_Age = age;}
private:T m_Name;K m_Age;
};// 1.全局函数在类内实现
void test1()
{Person<string, int> p("Rose", 18);printPerson(p);
}// 2.全局函数在类外实现
void test2()
{Person<string, int> p("Jack", 20);printPerson2(p);
}
int main()
{//test1();test2();system("pause");return 0;
}
1.3.9类模板案例
#pragma once
#include<iostream>
using namespace std;template<class T>
class MyArray
{
public:MyArray(int capacity){this->m_Capacity = capacity;this->m_Size = 0;this->pAddress = new T[this->m_Capacity];}//析构函数~MyArray(){if (this->pAddress != NULL) {delete[] this->pAddress;this->pAddress = NULL;}}//拷贝构造MyArray(const MyArray &arr) {this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;//this->pAddress = arr.pAddress;//深拷贝this->pAddress = new T[arr.m_Capacity];//将arr中的数据都拷贝过来for (int i = 0;i < this->m_Size;i++){this->pAddress[i] = arr.pAddress[i];}}// operator== 防止浅拷贝问题MyArray& operator==(const MyArray& arr){//先判断原来堆区是否有数据,如果有先释放if (this->pAddress != NULL){delete[] this->pAddress;this->pAddress = NULL;this->m_Capacity = 0;this->m_Size = 0;}//深拷贝this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;this->pAddress = new T[arr.m_Capacity];for(int i = 0;i<this->m_Size;i++){this->m_Capacity[i] = arr.m_Capacity[i];}return *this;}//尾插法void Push_Back(const T &val){// 判断容量是否有空间if (this->m_Capacity == this->m_Size){return;}this->pAddress[this->m_Size] = val;//更新数组大小this->m_Size++;}//尾删法void Pop_Back(){//让用户访问不到最后一个元素,即为尾删,逻辑删除if (this->m_Size == 0){return;}this->m_Size--;}//通过下标方式访问数组的元素T& operator[](int index){return this->pAddress[index];}//返回数组的容量int getCapacity(){return this->m_Capacity;}//返回数组的大小int getSize(){return this->m_Size;}//遍历数组void printArray(){for (int i = 0;i < this->m_Size;i++){cout << this->pAddress[i] << " ";}cout << endl;}private:// 指针指向堆区开辟的真实数组T* pAddress;// 数据容量int m_Capacity;//数据大小int m_Size;
};
#include<iostream>
using namespace std;
#include "MArray.hpp"void printArray(MyArray<int> &arr)
{for (int i = 0;i < arr.getSize();i++){cout << arr[i]<< " ";}cout << endl;
}void test1()
{MyArray<int> arr(5);for (int i = 0;i < 5;i++) {//利用尾插法插入数据arr.Push_Back(i);}printArray(arr);cout << "arr的容量为: " << arr.getCapacity() << endl;cout << "arr的容量为: " << arr.getSize() << endl;MyArray<int> arr2(arr);cout << "----arr2的打印输出----" << endl;printArray(arr2);arr2.Pop_Back();cout << "----arr2尾删后打印输出----" << endl;printArray(arr2);cout << "arr2的容量为: " << arr.getCapacity() << endl;cout << "arr2的容量为: " << arr.getSize() << endl;//遍历数组arr2.printArray();}int main()
{test1();system("pause");return 0;}
2.STL初始
2.1STL的诞生
- C++的面对对象和泛型编程思想,目的就是复用性的提升
- 大多数情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
- 为了建立数据结构和算法的一套标准,诞生了STL
2.2STL的基本概念
- STL(Standard Template Library,标准模板库)
- STL从广义上分为容器(container)、算法(algorithm)、迭代器(iterator)
- 容器和算法之间通过迭代器进行无缝连接
- STL几乎所有的代码都采用了模板类或模板函数
2.3STL六大组件
- STL分为六大组件
- 容器:各种数据结构,如vector、list、deque、set、map等等,用来存放数据
- 算法:各种常用的算法,如sort、find、copy、for_each等
- 迭代器:扮演了容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或仿函数或迭代器接口的东西
- 空间配置器:负责空间的配置与管理
2.4STL容器、算法、迭代器
2.4.1容器
- STL容器就是运用最广泛的一些数据结构实现出来
- 常用的数据结构
- 数组
- 链表
- 树
- 栈
- 队列
- 集合
- 映射表
- 容器分为序列式容器和关联式容器
- 序列式容器:强调值得排序,序列式容器中得每个元素均有固定得位置
- 关联式容器:二叉树结构,各元素之间没用严格得物理上的顺序关系
2.4.2算法
- 有限的步骤,解决逻辑或数学上的问题
- 算法分为: 质变算法和非质变算法
- 质变算法:是指运算过程中会更改区间内的元素内容,如拷贝、替换、删除
- 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
2.4.3迭代器
- 容器和算法之间粘合剂
- 提供一种方法,使之能够依序寻找某个容器所含的各个元素,而又无暴露该容器的内部表示方式
- 每个容器都有自己专属的迭代器
- 迭代器使用非常类似于指针
- 迭代器种类:
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,支持++、==、!= |
输出迭代器 | 对数据的只写访问 | 只写,支持++ |
向前迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
双线迭代器 | 读写操作,并能向前和向后操作 | 读写,支持++、-- |
随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、--、[n]、-n、<、<=、>、>= |
- 常用的容器迭代器种类为双向迭代器和随机访问迭代器
2.5容器算法迭代器初识
2.5.1 vector存放内置数据类型
- 容器
vector
- 算法
for_each
- 迭代器
vector<int>::iterator
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// vector容器存放内置数据类型//遍历
void printContainer(int val)
{cout << val<<" ";
}void test1()
{//创建了一个vector容器vector<int> vec;//向容器中插入数据vec.push_back(10);vec.push_back(20);vec.push_back(30);vec.push_back(50);vec.push_back(90);vec.push_back(20);// 通过迭代器访问容器中的数据// 起始迭代器 指向容器中第一个元素vector<int>::iterator itBegin = vec.begin();// 结束迭代器 指向容器中最后一个元素的下一个位置vector<int>::iterator itEnd = vec.end();//第一种遍历方式cout << "while循环遍历" << endl;while (itBegin != itEnd){ cout << *itBegin<< " ";itBegin++;}cout << endl;//第二种遍历方式cout << "for循环遍历容器" << endl;for (vector<int>::iterator it = vec.begin();it != vec.end();it++){ cout << *it<<" ";}cout << endl;//第三种遍历方式for_each(vec.begin(), vec.end(), printContainer);}int main()
{test1();system("pause");return 0;
}
2.5.2vector存放自定义数据类型
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;// vector容器存放自定义数据类型// 自定义数据类型
class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};//遍历
void printInfo(Person person)
{cout << "标签: " << person.m_Name << ",年限: " << person.m_Age << endl;
}void test1()
{vector<Person> vp;Person p1("数字化", 10);Person p2("模块化", 23);Person p3("拟人化", 1);Person p4("智能化", 9);Person p5("树脂化", 24);Person p6("机械化", 11);//向容器添加数据vp.push_back(p1);vp.push_back(p2);vp.push_back(p3);vp.push_back(p4);vp.push_back(p5);vp.push_back(p6);//遍历容器中的数据// while方式遍历cout << "-----------while遍历---------" << endl;vector<Person>::iterator itBegin = vp.begin();vector<Person>::iterator itEnd = vp.end();while (itBegin != itEnd){cout << "标签: " << (*itBegin).m_Name << ",年限: " << (*itBegin).m_Age << endl;itBegin++;}// for方式遍历cout << "--------for遍历-------" << endl;for (vector<Person>::iterator itBegin = vp.begin();itBegin != vp.end();itBegin++){cout << "标签: " << (*itBegin).m_Name << ",年限: " << (*itBegin).m_Age << endl;}//for_each方式遍历cout << "--------for_each遍历-------" << endl;for_each(vp.begin(), vp.end(), printInfo);
}
int main()
{test1();system("pause");return 0;
}
2.5.3容器嵌套
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// vector容器存放内置数据类型//遍历
void printContainer(int val)
{cout << val<<" ";
}void test1()
{//创建了一个vector容器vector<int> vec;//向容器中插入数据vec.push_back(10);vec.push_back(20);vec.push_back(30);vec.push_back(50);vec.push_back(90);vec.push_back(20);// 通过迭代器访问容器中的数据// 起始迭代器 指向容器中第一个元素vector<int>::iterator itBegin = vec.begin();// 结束迭代器 指向容器中最后一个元素的下一个位置vector<int>::iterator itEnd = vec.end();//第一种遍历方式cout << "while循环遍历" << endl;while (itBegin != itEnd){ cout << *itBegin<< " ";itBegin++;}cout << endl;//第二种遍历方式cout << "for循环遍历容器" << endl;for (vector<int>::iterator it = vec.begin();it != vec.end();it++){ cout << *it<<" ";}cout << endl;//第三种遍历方式for_each(vec.begin(), vec.end(), printContainer);}int main()
{test1();system("pause");return 0;
}
3.STL-常用容器
3.1string容器
3.1.1string基本概念
- 本质:string是c++风格的字符串,而string本质上是一个类
- string和char *区别
- char * 是一个指针
- string是一个类,类内部封装了char * ,管理这个字符串,是一个char *型的容器
3.1.2string构造函数
- 构造函数原型
- 创建空字符串
string();
- 字符串s初始化
string(const char * s);
- 使用一个字符串初始化另一个字符串
string(const string & str);
- 使用n个字符c初始化
string(int n,char c);
#include<iostream>using namespace std;
//string构造函数void test1()
{//1.默认构造函数string s1;//2.字符串初始化const char* str = "hello";string s2(str);cout << "s2 = " << s2 << endl;//3.一个字符串给另一个字符串初始化string s3(s2);cout << "s3 = " << s3 << endl;//使用n个字符初始化;string s4(4, 'x');cout << "s4 = " << s4 << endl;}int main()
{test1();system("pause");return 0;
}
3.1.3string赋值操作
- 给string字符串进行赋值
- 赋值的函数原型
- char*类型字符串 赋值给当前的字符串
string& operator=(const char *s)
- 把字符串s赋值给当前字符串
string& operator=(const string &s)
- 字符赋值给当前字符串
string& operator=(char c)
- 把字符串赋值给当前的字符串
string & assign(const char *s)
- 把字符串s的前面n个字符赋给当前字符串
string& assign(const char * s,int n)
- 把字符串s赋给当前字符串
string& assgin(const string &s)
- 用n个字符c赋给当前字符串
string& assign(int n,char c)
#include<iostream>
using namespace std;
#include<string>
//string赋值操作void test1()
{string str1;str1 = "hello";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(4, 'g');cout << "str7 = " << str7 << endl;
}int main()
{test1();system("pause");return 0;
}
3.1.4string字符拼接
- 实现在字符串末尾拼接字符串
- 函数原型
- 重载+=操作符
string& operator+=(const char* str) string& operator+=(const char c) string& operator+=(const string& str)
- 把字符串s连接到当前字符串结尾
string& append(const char *s)
- 把字符串s的前n个字符连接到当前字符串结尾
string& append(const char*s,int n)
- 同operator+=(const string &str)
string& append(const string &s)
- 字符串中从pos开始的n个字符连接到字符换末尾
string& append(const string &s,int pos,int n)
str7 += " bbb";cout << "str7 = " << str7 << endl;str7.append("hello", 2, 2);cout << "str7 = " << str7 << endl; // gggg bbbll
3.1.5string查找和替换
- 查找: 查找指定字符串是否存在
- 替换: 在指定的位置替换字符串
- 函数原型
- 查找str字符串第一次出现位置,从pos开始查找
int find(const string &str,int pos = 0) const
- 查找s第一次出现位置,从pos开始查找
int find(const char* s,int pos = 0) const
- 从pos位置查找s的前n个字符第一次位置
int find(const char &s,int pos,int n) const
- 查找字符c第一次出现的位置
int find(const char c,int pos = 0) const
- 查找str最后一次位置,从pos开始查找
int rfind(const string &str,int pos = npos) const
- 查找s最后一次出现位置,从pos开始查找
int rfind(const char * s,int pos = npos) const
- 从pos查找s的前n个字符最后一次位置
int rfind(const char &s,int pos,int n) const
- 查找字符c最后一次出现的位置
int rfind(const char c,int pos = 0) const
- 替换从pos开始n个字符为字符串
string& replace(int pos,int n,const string& str)
- 替换从pos开始的n个字符为字符串s
string& replace(int pos,int n,const char* s)
3.1.6string字符串比较
- 字符串之间的比较(逐个比较)
- 比较方式
- 字符串比较是按字符的ASCII码进行对比
- 结果
- = 返回 0
- > 返回 1
- < 返回 -1
- 函数原型
- 与字符串s比较
int compare(const string &s) const
- 与字符串s比较
int compare(const char * s) const
string strs = "hello";string strs1 = "hollw";int comp = strs.compare(strs1);cout << "comp = " << comp << endl;// -1
3.1.7string字符存取
- string中单个字符存取
- 通过[ ]方式获取字符
char& operator[](int n)
- 通过at方式获取字符
char& at(int n)
string strs = "hello";
cout << "字符: " << strs[2] << endl;// l
3.1.8string插入和删除
- 对string字符串进行插入和删除字符串操作
- 函数原型
- 插入字符串
string& insert(int pos,const char* s)
- 插入字符串
string& insert(int pos,const string& str)
- 在指定位置插入n个字符c
string& insert(int pos,int n,char c)
- 删除从pos开始的n个字符
string& erase(int pos,int n = npos)
string strs = "hello";string str12;str12 = strs.erase(1, 3);cout << "str12 = " << str12 << endl;// ho
3.1.9string子串
- 从字符串中获取想要的子串
- 函数原型
- 返回pos开始的n个字符组成的字符串
string substr(int pos = 0,int n = npos) const
string strs = "hello";string stru;stru = strs.substr(1, 3);cout << "stru = " << stru << endl;// ell
3.2vector容器
3.2.1vector基本概念
1.功能
- vector数据结构和数组非常类似,也称为单端数组
2.vector与普通数组区别
- 不同之处在于数组是静态空间,而vector可以动态扩展
3.动态扩展
- 并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间释放原空间
- vector容器的迭代器是支持随机访问的迭代器
3.2.2vector构造函数
- 创建vector容器
- 函数原型
- 采用模板实现类实现,默认构造函数
vector<T> v;
- 将v[begin(),end()]区间中的元素拷贝给本身
vector(v.begin(),v.end())
- 构造函数将n个elem拷贝给本身
vector(n.elem)
- 拷贝构造函数
vector(const vector &vec)
#include<iostream>
using namespace std;
#include<vector>void printVector(vector<int> &vec)
{for (vector<int>::iterator it = vec.begin();it != vec.end();it++){cout << *it << " ";}cout << endl;
}
void test1()
{//默认无参构造函数vector<int> v1;for (int i = 0;i < 10;i++){v1.push_back(i + 1);}printVector(v1);// 通过区间方式进行构造vector<int> v2(v1.begin(), v1.end());printVector(v2);//n个elem方式构造vector<int> v3(4, 88);printVector(v3);//拷贝构造vector<int> v4(v3);printVector(v4);
}int main()
{test1();system("pause");return 0;
}
3.2.3vector赋值操作
- 给vector容器赋值操作
- 函数原型
- 重载等号操作符
vector& operator=(const vector &vec)
- 将[begin,end)区间中的数据拷贝赋值给本身
assign(beg,end)
- 将n个element拷贝赋值给本身
assign(n,elem)
//赋值 operator=vector<int> v11;v11 = v4;printVector(v11);//assignvector<int> v12;v12.assign(v4.begin(), v4.end());printVector(v12);
3.2.4vector容量和大小
- 对vector容器的容量和大小操作
- 函数原型
- 判断容器是否为空
empty()
- 容器容量
capacity()
- 返回容器中元素的个数
size()
- 重新指定容器的长度为num,若容器长度变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
resize(int num)
- 重新指定容器的长度num,若容器变长,则elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
resize(int num,elem)
cout << "容量:" << v12.capacity() << endl;// 4cout << "容量:" << v12.size() << endl;// 4v12.resize(8);cout << "容量:" << v12.capacity() << endl;// 8cout << "容量:" << v12.size() << endl;// 8
3.2.5vector插入和删除
- 对vector容器进行插入、删除操作
- 函数原型
- 尾部插入元素ele
push_back(ele)
- 删除最后一个元素
pop_back()
- 迭代器指向位置pos插入元素ele
insert(const_iterator pos,ele)
- 迭代器指向位置pos插入count个元素ele
insert(const_iterator pos,int count,ele)
- 删除迭代器指向的元素
erase(const_iterator pos)
- 删除迭代器从start到end之间的元素
erase(const_iterator start,const_iterator end)
- 删除容器中所有元素
claer()
v12.push_back(78);cout << "容量:" << v12.capacity() << endl;// 12cout << "容量:" << v12.size() << endl;// 9printVector(v12);//插入迭代器 v12.insert(v12.begin(), 10);printVector(v12);
3.2.6vector数据存取
- 对vector中的数据存取操作
- 函数原型
- 返回索引idx所指的数据
at(int idx)
- 返回索引idx所指的数据
operator[idx]
- 返回容器中第一个数据元素
front()
- 返回容器中最后一个数据元素
back()
3.2.7vector互换容器
- 实现两个容器内元素进行互换
- 函数原型
- 将vec与本身的元素互换
swap(vec)
//巧用swap收缩内存
v.resize(3);
vector<int>(v).swap(v)
3.2.8vector预留空间
- 减少vector在动态扩展容量时的扩展次数
- 函数原型
- 容器预留len个元素长度,预留位置不初始化,元素不可访问
reserve(int len)
3.3deque容器
3.3.1deque容器基本概念
- 双端数组,可以对头端进行插入删除操作
- deque与vector区别
- vector对于头部的插入删除效率低,数据量越大,效率越低
- deque相对而言,对头部的插入删除速度会比vector快
- vector访问元素时的速度会比deque快,这和两者内部实现有关
- deque内部工作原理
- deque内部有一个中控器,维护每段缓冲区中的内容,缓冲区中存放的真实数据
- 中控器维护的每个缓冲区的地址,使得使用deque时像一片连续的内存空间
- deque容器的迭代器也是支持随机访问的
3.3.2deque构造函数
- deque容器构造
- 函数原型
- 默认构造形式
deque<T> depT
- 构造函数将[beg,end)区间中的元素拷贝给本身
deque(beg,end)
- 构造函数将n个elem拷贝给本身
deque(n,elem)
- 拷贝构造函数
deque(const deque &dep)
#include<iostream>
#include<deque>
using namespace std;//添加const,避免值被修改
void printDeque(const deque<int> &dep)
{for (deque<int>::const_iterator it = dep.begin();it != dep.end();it++){cout << *it << " ";}cout << endl;
}void test1()
{//默认构造deque<int> dep;for(int i = 0;i< 10;i++){dep.push_back(i + 1);}printDeque(dep);deque<int> dep2(dep.begin(), dep.end());printDeque(dep2);
}int main()
{test1();system("pause");return 0;
}
3.3.3deque赋值操作
- 给deque容器进行赋值
- 函数原型
- 重载等号操作符
deque& operato=(const deque &dep)
- 将[beg,end)区间中的数据拷贝赋值给本身
assign(beg,end)
- 将n个elem拷贝赋值给本身
assign(n,elem)
3.3.4deque大小操作
- 对deque容器大小进行操作
- 函数原型
- 判断容器是否为空
empty()
- 返回容器中元素的个数
size()
- 重新指定容器的长度为num,若容器变长,则以默认值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
resize(num)
- 重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
resize(num.elem)
3.3.5deque插入删除
- 向deque容器中插入删除元素
- 函数原型
- 容器尾部添加一个元素
push_back(elem)
- 在容器头部插入一个元素
push_front(elem)
- 删除容器最后一个元素
pop_back()
- 删除容器第一个元素
pop_front()
- 在pos位置插入一个elem元素的拷贝,返回新数据的位置
insert(const_iterator pos,elem)
- 在pos位置插入n个elem数据,无返回值
insert(const_iterator pos,n,elem)
- 在pos位置插入[beg,end)区间的数据,无返回值
insert(const_iterator pos,beg,end)
- 清空容器的所有数据
clear()
- 删除[beg,end)区间的数据,返回下一个数据的位置
erase(beg,end)
- 删除pos位置的数据,返回下一个数据的位置
erase(const_iterator pos)
// 在dep1前面插入dep2
dep1.insert(dep1.begin(),dep2.begin(),dep2.end())
3.3.6deque数据存取
- 对deque中的数据存取操作
- 函数原型
- 返回所有idx所指的数据
at(idx)
- 返回索引idx所指的数据
operator[]
- 返回容器中第一数据元素
front()
- 返回容器中最后一个数据元素
back()
3.3.7deque排序
- 利用算法实现对deque容器进行排序
- 算法
- 对beg和end区间内元素进行排序
sort(iterator beg,iterator end)
3.4案例
3.5stack容器
3.5.1stack基本概念
- stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口
- 栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为
- 栈中进入数据称为 ------- 入栈
push
- 栈中弹出数据称为 ------- 出栈
pop
3.5.3stack常用接口
1.构造函数
- stack采用模板类实现,stack对象默认构造函数
stack<T> stk;
- 拷贝构造函数
stack(const stack& stack)
2.赋值操作
- 重载等号操
stack& operator=(const stack& stack)
3.数据存取
- 向栈顶添加元素
push(elem)
- 从栈顶移除第一个元素
pop()
- 返回栈顶元素
top()
4.大小操作
- 判断堆栈是否为空
empty()
- 返回栈的大小
size()
3.6queue容器
3.6.1queue基本概念
-
queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口
-
队列容器允许从一端新增数据,另一端移除数据
-
队列中只有队头和队尾可以被外界使用,因此队列不允许有遍历行为
-
队列中进数据称为 — 入队
push
-
队列中出数据称为 — 出队
pop
3.6.2queue常用接口
- 栈容器常用的对外接口
1.构造函数
- queue采用模板类实现,queue对象的默认构造形式
queue<T> que;
- 拷贝函数
queue(const queue &que)
2.赋值操作
- 重载等号操作符
queue& operator=(const queue &que)
3.数据存取
- 往队尾添加元素
push(elem)
- 从队头移除第一个元素
pop()
- 返回最后一个元素
back()
- 返回第一个元素
front()
4.大小操作
- 判断堆栈是否为空
empty()
- 返回栈的大小
size()
3.7list容器
3.7.1list基本概念
- 将数据进行链式存储
- 链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
- 链表的组成
- 链表是由一系列结点组成
- 每一个结点有一个存储数据元素的数据域和一个存储下一个结点地址的指针域
- STL中的链表是一个双向循环链表
- 链表的存储方式并不是连续的内存空间,链表list中的迭代器只支持前移和后移,属于双向迭代器
- list的优点
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
- list的缺点
- 链表灵活,但是空间(指针域)和时间(遍历)额外消耗较大
3.7.2list构造函数
- 创建list容器
- 函数原型
- list采用模板类实现,对象默认构造形式
list<T> lst;
- 构造函数将[beg,end)区间中的元素拷贝给本身
list(beg,end)
- 构造函数将n个elem拷贝给本身
list(n,elem)
- 拷贝构造函数
list(const list &lst)
#include<iostream>
#include<list>
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 test1()
{list<int> lst;lst.push_back(20);lst.push_back(22);lst.push_back(21);lst.push_back(29);lst.push_back(90);printList(lst);list<int> lst1(lst.begin(), lst.end());printList(lst1);list<int> lst2(5, 30);printList(lst2);list<int> lst3;lst3 = lst;printList(lst3);
}int main()
{test1();system("pause");return 0;
}
3.7.3list赋值和交换
- 给list容器进行赋值,以及交换list容器
- 函数原型
- 将[beg,end)区间中的数据拷贝赋值给本身
assign(beg,end)
- 将n个elem拷贝给本身
assign(e,elem)
- 重载等号操作符
list& operator=(const list &lst)
- 将lst与本身的元素互换
swap(lst)
list<int> lst4;lst4.assign(lst.begin(), lst.end());printList(lst4);lst4.swap(lst2);printList(lst2);printList(lst4);
3.7.4list大小操作
- 对list容器的大小操作
- 函数原型
- 返回容器中元素的个数
size()
- 判断容器是否为空
empty()
- 重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
resize(num)
- 重新指定容器的长度为num,若容器变长,则以elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
resize(num,elem)
3.7.5list插入和删除
- 对list容器进行数据的插入和删除
- 函数原型
- 在容器尾部加入一个元素
push_back(elem)
- 删除容器最后一个元素
pop_back()
- 在容器开头插入一个元素
push_front(elem)
- 从容器开头移除第一个元素
pop_front()
- 在pos位置插入elem元素的拷贝,返回新数据的位置
insert(pos,elem)
- 在pos位置插入n个elem数据,无返回值
insert(pos,n,elem)
- 在pos位置插入[beg,end)区间的数据,无返回值
insert(pos,beg,end)
- 移除容器的所有数据
clear()
- 删除[beg,end)区间的数据,返回下一个数据的位置
erase(beg,end)
- 删除pos位置的数据,返回下一个数据的位置
erase(pos)
- 删除容器中所有与elem值匹配的元素
remove(elem)
lst4.insert(lst4.begin(), 4, 22);printList(lst4);lst4.pop_back();printList(lst4);
3.7.6list数据存取
- 对list容器中数据进行存取(不支持随机访问)
- 函数原型
- 返回第一个元素
front()
- 返回最后一个元素
back()
//验证迭代器是否支持随机访问
list<int>::iterator it = lst.begin();
it++;
it--;
// it = it + 1;//error,不支持随机访问
3.7.7list反转和排序
- 将容器中的元素反转,以及将容器中的数据进行排序
- 函数原型
- 反转链表
reverse()
- 链表排序
sort()
- 所有不支持随机访问迭代器的容器,不支持用标准算法
lst.sort()
3.8set/multiset容器
3.8.1set基本概念
1.简介
- 所有元素都会在插入时自动被排序
2.本质
- set/multiset属于关联式容器,底层结构是用二叉树实现
3.set和multiset区别
- set不允许容器存中有重复的元素
- multiset允许容器中有重复的元素
#include<iostream>
using namespace std;
#include<set>void printSet(const set<int> &st)
{for(set<int>::const_iterator it = st.begin();it != st.end();it++){cout << *it << " ";}cout << endl;
}void test()
{set<int> st;st.insert(10);st.insert(20);st.insert(50);st.insert(40);st.insert(30);st.insert(30);cout << "遍历容器" << endl;printSet(st);// 10 20 30 40 50
}int main()
{test();system("pause");return 0;
}
3.8.2set构造和赋值
- 创建set容器以及赋值
- 函数原型
- 构造
- 默认构造函数
set<T> st
- 拷贝构造函数
set(const set &st)
- 赋值
- 重载等号操作符
set& operator=(const set &st)
- 构造
3.8.3set大小和交换
- 统计set容器大小以及交换set容器
- 函数原型
- 返回容器中元素的数据
size()
- 判断容器是否为空
empty()
- 交换两个集合容器
swap()
3.8.4set插入和删除
- set容器进行插入数据和删除数据
- 函数原型
- 在容器中插入元素
insert(elem)
- 清除所有元素
clear()
- 删除pos迭代器所指元素,返回下一个元素的迭代器
erase(pos)
- 删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(beg,end)
- 删除容器中值为elem的元素
erase(elem)
set<int>::iterator it = st.begin();
it++;
set<int>::iterator its = st.erase(it);
cout << "下一个元素的迭代器:" << *its << endl;// 30
3.8.5set查找和统计
- 对set容器进行查找数据以及统计
- 函数原型
- 查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end()
find(key)
- 统计key的元素个数
count(key)
set<int>::iterator it1= st.find(20);if (it1 != st.end()){cout << "找到元素:" << *it1 << endl;}else{cout << "未找到该元素" << endl;}int num = st.count(30);cout << "元素的个数:" << num << endl;// 1
3.8.6set和multiset区别
- 区别
- set不可以插入重复数据,而multiset
- set插入数据的同时会返回插入结果,表示是否插入成功
- multiset不会检测数据,因此可以插入重复数据
3.8.7pair对组创建
- 成对出现的数据,利用对组可以返回两个数据
- 两种创建方式
pair<type,type> p (value1,value2) pair<type,type> p = make_pair(value1,value2)
#include<iostream>
using namespace std;void test()
{//第一种方式pair<string, int> p("Tom", 22);cout << "姓名:" << p.first << ",年龄:" << p.second << endl;//第二种方式pair<string, int> p2 = make_pair("Jerry", 18);cout << "姓名:" << p2.first << ",年龄:" << p2.second << endl;
}int main()
{test();system("pause");return 0;
}
3.8.8set容器排序
- set容器默认排序规则为从小到大,改变排序规则
- 技术点
- 利用仿函数,可以改变排序规则
#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;//添加const,避免值被修改
void printDeque(const deque<int> &dep)
{for (deque<int>::const_iterator it = dep.begin();it != dep.end();it++){cout << *it << " ";}cout << endl;
}void test1()
{//默认构造deque<int> dep;for(int i = 0;i< 10;i++){dep.push_back(i + 1);}printDeque(dep);deque<int> dep2(dep.begin(), dep.end());printDeque(dep2);sort(dep2.begin(), dep2.end());printDeque(dep2);
}int main()
{test1();system("pause");return 0;
}
3.9map/multimap容器
3.9.1map基本概念
1.简介
- map中所有元素都是pair
- pair中第一个元素为key(键值),起索引作用;第二个元素为value(实值)
- 所有元素都会根据元素的键值自动排序
2.本质
- map/multimap属于关联式容器,底层结构使用二叉树实现
3.优点
- 可以根据key值快速找到value值
4.map和multimap区别
- map不允许容器中重复key值元素
- multimap允许容器中重复key值元素
3.9.2map构造和赋值
- 对map容器进行构造和赋值操作
- 函数原型
- ma默认构造函数
map<T1,T2> mp;
- 拷贝构造函数
map(const map &mp)
- 重载等号操作符
map &operator=(const map& mp)
map<string,int> mp;
mp.insert(pair<string,int> ("Tom",18));
3.9.3map大小和交换
- 统计map容器的大小以及交换map容器
- 函数原型
- 返回容器中元素的数目
size()
- 判断容器是否为空
empty()
- 交换两个集合
swap()
3.9.4插入和删除
- map容器进行插入数据和删除数据
- 函数原型
- 在容器中插入元素
insert(elem)
- 清除所有元素
clear()
- 删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(pos)
- 删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(beg,end)
- 删除容器中值为key的元素
erase(key)
3.9.5map查找和统计
- 对map容器进行查找数据以及统计数据
- 函数原型
- 查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end()
find(key)
- 统计key的元素个数
count(key)
3.9.6map容器排序
- map容器默认排序规则为按照key值进行 从小到大排序
- 技术点
- 利用仿函数,可以改变排序规则
#include<iostream>
using namespace std;
#include<map>
#include<string>class MyCompare
{
public:bool operator()(string str1,string str2)const{return str1 > str2;}
};void test()
{map<string, int,MyCompare> mp;//mp.insert(pair<string, int>("数字化", 13));mp.insert(make_pair("数字化", 13));//同上mp.insert(make_pair("模块化", 31));mp.insert(make_pair("智能化", 20));mp.insert(make_pair("系统化", 8));mp.insert(make_pair("助手化", 22));for (map<string, int>::iterator it = mp.begin();it != mp.end();it++){cout << "项目名称: " << (*it).first << ",项目年限:" << it->second << endl;}
}int main()
{test();system("pause");return 0;
}
相关文章:

C++提高编程(1)
C提高编程1.模板1.1模板的概念1.2函数模板1.2.1函数模板语法1.2.2函数模板注意事项1.2.3函数模板案例1.2.4普通函数与函数模板的区别1.2.5普通函数与函数模板的调用规则1.2.6模板的局限性1.3类模板1.3.1类模板语法1.3.2类模板和函数模板区别1.3.3类模板中成员函数创建时机1.3.4…...

day26 回溯算法的部分总结
回溯算法的部分总结 回溯算法是一种常用于解决排列组合问题、搜索问题的算法,它的基本思想是将问题的解空间转化为一棵树,通过深度优先搜索的方式遍历树上的所有节点,找到符合条件的解。回溯算法通常使用递归实现,每次递归时传入…...

带你玩转Python爬虫(胆小者勿进)千万别做坏事·······
这节课很危险,哈哈哈哈,逗你们玩的 目录 写在前面 1 了解robots.txt 1.1 基础理解 1.2 使用robots.txt 2 Cookie 2.1 两种cookie处理方式 3 常用爬虫方法 3.1 bs4 3.1.1 基础介绍 3.1.2 bs4使用 3.1.2 使用例子 3.2 xpath 3.2.1 xpath基础介…...

【JavaScript 】严格模式,With关键字,测试框架介绍,assert
❤️ Author: 老九 ☕️ 个人博客:老九的CSDN博客 🙏 个人名言:不可控之事 乐观面对 😍 系列专栏: 文章目录静态类型语言弱类型严格模式将过失错误转化为异常简化变量的使用With测试框架try-catch选择性捕获…...

mybatis实现一个简单的CRUD功能的小案例(后端)编写流程
下面是一个使用mybatis实现增删改查功能的示例程序: 1.创建一个数据库 首先需要创建一个名为test_db的数据库,里面包含一个名为user_info的表,其中包含id、name、age三个字段。 2.配置mybatis 在项目的pom.xml文件中添加mybatis和mysql依…...

腾讯云轻量应用服务器价格表(2023版)
2023腾讯云轻量应用服务器2核2G4M带宽88元一年、2核4G6M带宽159元/年、4核8G10M优惠价425元、8核16G14M价格1249、16核32G20M服务器2499元一年,今天分享2023腾讯云服务器配置及精准报价。 腾讯云轻量应用服务器优惠价格表 腾讯云服务器分为轻量应用服务器和云服务器…...

网络层IP协议和数据链路层
目录IP协议协议头格式分片网段划分特殊的IP地址IP地址的数量限制NAT技术NAT技术背景NAT IP转换过程NAPTNAT技术的缺陷NAT和代理服务器私有IP地址和公网IP地址路由路由表生成算法数据链路层认识以太网以太网帧格式认识MAC地址对比理解MAC地址和IP地址认识MTUMTU对IP协议的影响MT…...

零基础学习Java 03
目录 数组 动态初始化数组 静态初始化 数组的应用 数组两种典型的异常 length关键字求出数组的长度 数组遍历在IDEA中输出快捷语句 对象数组 数组的遍历:foreach方法 二维数组 枚举(enum) 数组 1在方法中可以返回一个数组,但是在定义方法时类型要…...

PG数据库超时退出 TCP设定
数据库在使用psql工具以及jdbc进行远程连接时,在经过一定时间之后报错-致命错误: terminating connection due to client no input timeout。 排查安全参数,hg_clientnoinput 0; 问题原因 操作系统TCP相关参数设置不正确&…...

每日学术速递4.4
CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CL 1.Baize: An Open-Source Chat Model with Parameter-Efficient Tuning on Self-Chat Data 标题:Baize:一种对自聊天数据进行参数高效调优的开源聊天模型 作者…...

ChatGPT将引发大量而普遍的网络安全隐患
ChatGPT是一个基于人工智能的语言生成模型,它可以在任何给定的时间,使用自然语言生成技术,生成文本、对话和文章。它不仅可以被用来编写文本,还可以用来编写语言、生成图像和视频。目前, ChatGPT已广泛应用于语言翻译、…...

购买学生护眼台灯几瓦最好?有哪些推荐护眼灯
现今的近视已然成为普遍现象,而且有往低年龄段发展的趋势。究其原因,长期使用电子设备是一方面,还是就是我们日常工作、学习、生活没有很好的护眼环境,很多时候我们不经意的错误习惯,久而久之就有可能诱发近视。对孩子…...

什么是 SYN 攻击?如何避免 SYN 攻击?
SYN 攻击方式最直接的表现就会把 TCP 半连接队列打满,这样当 TCP 半连接队列满了,后续再在收到 SYN 报文就会丢弃,导致客户端无法和服务端建立连接。 避免 SYN 攻击方式,可以有以下四种方法: 调大 netdev_max_backlo…...

数据分析练习——学习一般分析步骤
目录 一、准备工作 二、导入库和数据 1、导入必要的库: 2、模拟数据 三、数据分析过程 1、读取数据: 2、数据概览和描述性统计: 2.1、查看数据概览: 2.2、查看描述性统计: 3、数据清洗: 3.1、处…...

Linux环境下挂载exfat格式U盘,以及安装exfat文件系统
目录Linux一般支持的文件系统有:1.安装exfat软件安装工具环境以及exfat件依赖的系统软件下载exfat源码包并安装2.挂载exfat格式U盘查看U盘在那个目录执行挂载命令Linux一般支持的文件系统有: 文件系统名称详情ext专门为Linux核心做的第一个文件系统&…...

网格布局grid
grid网格定义 css网格是一个用于web的二维(行和列的组合)布局,利用网格,你可以把内容按照行和列的格式进行排版,另外,可以轻松的实现复杂布局。 1.定义网格和fr单位 1.1定义网格 在父元素加上ÿ…...

《扬帆优配》环境更优!这类资金,迎利好!
近来,第一批主板注册制新股连续发动申购,网下询价中,组织出资者频繁现身打新商场,公募基金、社保基金、养老金、保险资金等中长时间资金,成为全面注册制下新股发行商场的重要参加者。 多位业内人士对此表明,…...

RK3568平台开发系列讲解(内存篇)内存管理的相关结构体
🚀返回专栏总目录 文章目录 一、硬件架构二、Linux 物理内存管理结构体沉淀、分享、成长,让自己和他人都能有所收获!😄 📢应用程序想要使用内存,必须得先找操作系统申请,我们有必要先了解一下 Linux 内核怎么来管理内存,这样再去分析应用程序的内存管理细节的时候,…...

如何理解二叉树与递归的关系
二叉树一般都是和递归有联系的,二叉树的遍历包括了前序,后序,中序,大部分题目只要考虑清楚应该用那种遍历顺序,然后特殊情况的条件,题目就会迎刃而解。 1. 先来说说二叉树的遍历方式 其实二叉树的遍历很简…...

CSS 高级技巧
目录 1.精灵图 1.1为什么需要精灵图 1.2 精灵图(sprites)的使用 2.字体图标 2.1字体图标的产生 2.2字体图标的优点 2.3字体图标的下载 2.4字体图标的引入 2.5字体图标的追加 1.精灵图 1.1为什么需要精灵图 一个网站往往回应用很多的小背景图像作…...

ToBeWritten之MIPS汇编基础铺垫
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 转移发布平台通知:将不再在CSDN博客发布新文章,敬…...

MySQL数据库对数据库表的创建和DML操作
1.创建表user,其中包含id、name、password,其中主键自增,name,唯一是可变长度,最大长度是30,密码,可变长度,最大长度为20,不为空。 以下是创建符合要求的user表的SQL语句…...

【PCB专题】PCB 阻焊层(solder mask)与助焊层(paste mask)有什么区别
一块标准的印刷电路板 (PCB) 通常需要两种不同类型的“罩层 (mask)”。其中阻焊层 (solder mask) 和助焊层 (paste mask) 都是“罩层”,但在 PCB 制造过程中,它们分别用于两个完全不同的部分,因此也存在很大的区别。 阻焊层定义 阻焊层定义了电路板外表面的保护材料涂抹范围…...

ThreeJS-纹理旋转、重复(十一)
旋转 文档:three.js docs 关键代码: //设置旋转中心,默认左下角 docColorLoader.center.set(0.5,0.5); //围绕旋转中心逆时针旋转45度 docColorLoader.rotation Math.PI/4; 完整代码: <template> <div id"three_div"></div>…...
CSDN——Markdown编辑器——官方指导
CSDN——Markdown编辑器——官方指导欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表…...

DN-DETR调试记录
博主在进行DINO实验过程中,发现在提取了3个类别的COCO数据集中,DINO-DETR对car,truck的检测性能并不理想,又通过实验自己的数据集,发现AP值相差不大且较为符合预期,因此便猜想是否是由于DINO中加入了负样本约束导致背景…...

ASP消防网上考试系统设计与实现
本文以ASP和Access数据库来开发服务器端,通过计算机网络技术实现了一个针对消防部队警官的网上考试系统。为了。提高消防部队的工作效率和信息化水平,体现消防部队信息化进程的特色,开发一个适合消防部队的计算机网上考试系统是非常必要的。鉴…...

MongoDB - 数据模型的设计模式
简介 官方文章的地址是 Building with Patterns: A Summary,其中汇总了 12 种设计模式及使用场景。 上述的图表列举了 12 种设计模式及应用场景,主要是以下这些: 近似值模式(Approximation Pattern)属性模式…...

3D格式转换工具助力Shapr3D公司产品实现了 “无障碍的用户体验”,可支持30多种格式转换!
今天主要介绍的是HOOPS Exchange——一款支持30多种CAD文件格式读取和写入的工具,为Shapr3D公司提供的重要帮助! Shapr3D是一家有着宏伟目标的公司:将CAD带入21世纪!该公司于2016年首次推出其同名应用程序,并将Shapr3D带到了macOS…...

虚拟环境-----virtualenv和pipenv的安装和应用
1.pip install virtualenv 2.pip安装虚拟环境管理包virtualenvwrapper-win 3.创建一个存放虚拟环境的目录(建议命名为.env或者.virtualenv) 4.配置环境变量(变量名:WORKON_HOME,值:上面创建的目录路径) …...