主要针对 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-99cout << myadd(a, c) << endl;//函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换//myadd2(a, c); //报错,a和c不是同种类型//如果利用显示指定类型的方式,可以发生隐式类型转换myadd2<int>(a, c); //正确,指定了T为int,即所有的实参都会被自动转换为int型。}int main4() {test4();system("pause");return 0;
}
普通函数与函数模板调用规则
-
如果函数模板和普通函数都可以实现,优先调用普通函数。
-
可以通过空模板参数列表来强制调用函数模板。
-
函数模板也可以发生重载。
-
如果函数模板可以产生更好的匹配。优先调用函数模板。
-
<>内部为函数模板的参数,用来传入数据类型。 -
普通函数与函数模板最好不要同时出现。
实例 :
#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;
}
类模板对象做函数参数
目标 :类模板实例化的对象,向函数传参的方式。
一共有三种传入方式:
-
指定传入的类型 —— 直接显示成员的数据类型。
-
参数模板化 —— 将对象中的参数变为模板进行传递。
-
整个类模板化 —— 将这个对象类型模板化进行传递。
实例 :
#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 大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
-
容器 :各种数据结构,如
vector、list、deque、set、map等,用来存放数据。 -
算法 :各种常用的算法,如
sort、find、copy、for_each等。 -
迭代器 :扮演了容器与算法之间的胶合剂。
-
仿函数 :行为类似函数,可作为算法的某种策略。
-
适配器 :一种用来修饰容器或者仿函数或迭代器接口的东西。
-
空间配置器 :负责空间的配置与管理。
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; //创建一个容器vv.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 容器
基本概念
本质 :
string是C++风格的字符串,而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初始化。
实例 :
#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)//构造函数将n个elem拷贝给本身。 -
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)//将n个elem拷贝给本身。
实例 :
#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); //重新指定容器长度,并将多余位置用指定数字填充,默认为0printVector(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)//删除迭代器从start到end之间的元素。 -
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 容器基本概念
功能 :
- 双端数组,可以对头端进行插入删除操作。
deque 与 vector 区别:
-
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位置插入n个elem元素的拷贝,无返回值。 -
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)//对beg和end区间内元素进行排序。
实例 :
#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)//构造函数将n个elem拷贝给自身。 -
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)//将n个elem拷贝赋值给本身。 -
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)//删除迭代器从beg到end之间的元素,返回下一个数据的位置。 -
clear()//删除容器中所有元素。 -
remove(elem)//删除容器中所有与elem值匹配的元素。
7、set/multiset 容器
基本概念
简介 :
- 所有元素都会在插入时自动被排序。
本质 :
set/multiset属于 关联式容器 ,底层结构是用 二叉树 实现。
set 和 multiset 区别:
-
set不允许容器中有重复的元素。 -
multiset允许容器中有重复的元素。
8、map/multimap 容器
基本概念
简介 :
-
map中所有元素都是pair(队组),即成对出现的数据。 -
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)。 -
所有元素都会根据元素的键值自动排序。
本质 :
map/multimap属于 关联式容器 ,底层结构是用二叉树实现。
优点 :
- 可以根据
key值快速找到value值。
map 与 multimap 区别:
-
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():查找指定元素是否存在,返回值类型为boolbinary_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 ()(参数列表),在重载的时候,如果参数列表中只有一个元素称为一元谓词,两个称为二元谓词,在重载之后,由于是在类内重载,因此在算法使用的时候,有的算法需要传入的是函数对象,此时需要将谓词转化为函数对象,即使用类创建一个匿名对象。 - 注意 :使用函数对象的方法是,创建一个类,在这个类的内部重载运算符,以达到某种目的,由这种类所生成的对象称为函数对象,其主要用于在算法中作为参数以实现某种目的。