c++——提高

1.模板

1.1概念

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

1.2函数模板

  • C++的一种编程思想是:泛型编程,主要利用的技术是模板
  • C++提供两种模板机制:函数模板和类模板

函数模板作用:

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

语法:

template<typename T>
// 函数声明或定义
/*
解释:
	template:声明创建模板
	typename:表示其后面的符号是一种数据类型,可以用class代替
	T:通用的数据类型,名称可以替换,通常为大写字母
*/
// 普通的交换函数
void swapInt(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

void swapDouble(double& a, double& b) {
	double temp = a;
	a = b;
	b = temp;
}

// 使用模板的交换函数
// 先声明模板
template<typename T> // 这里可以用typename或class
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}

void test() {
	int a = 1;
	int b = 2;
	swapInt(a, b);
	cout << "a = " << a << "\tb = " << b << endl;

	double c = 1.1;
	double d = 2.2;
	swapDouble(c, d);
	cout << "c = " << c << "\td = " << d << endl;

	// 模板的交换函数的使用
	// 两种方式使用函数模板
	// 1.自动类型推导
	// mySwap(a, b);
	// 2.显示指定类型
	// mySwap<int>(a, b);
	// mySwap<double>(c, d);
	mySwap(a, b);
	mySwap(c, d);
	cout << "a = " << a << "\tb = " << b << endl;
	cout << "c = " << c << "\td = " << d << endl;

}

使用模板是为了提高代码复用性,将类型参数化

注意:

  • 自动类型推导,必须推导出的类型都一致才能使用。(就是说使用模板的函数,它的使用模板的类型都应该是一样的才可以)
  • 模板必须确定出T的数据类型,才可以使用。
template<typename T>
void func1(T a, T b){
}

template<typename T>  // 直接注释掉这句话也可以运行。 
void func2(){
}

void test(){
	int a = 1;
	char b = ' ';
	func1(); // 报错,因为a和b类型不一致
	func2(); // 报错,因为编译器确定不了T的类型。(func2<int>(); // 这样调用就不会报错,因为这样就直接告诉编译器T的类型了)
}
普通函数和函数模板的区别
  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换
// 普通函数
int myAdd1(int a, int b){
	return a+b;
}

// 函数模板
template<typename T>
T myAdd2(T a, T b){
	return a + b;
}

void test(){
	int a = 1;
	int b = 2;
	char c = 'a';
	// 正常的调用与返回
	cout << myAdd1(a,b) << endl;
	// 发生隐式类型转换,自动的把c转换为了int类型
	cout << myAdd1(a,c) << endl;
	// 报错
	cout << myAdd2(a,c) << endl;
	// 发生隐式类型转换,正常运行
	cout << myAdd2<int>(a,c) << endl;
	cout << myAdd2<char>(a, c) << endl;
}

推荐使用指定显示类型的方法来调用函数模板

普通函数与函数模板的调用规则
  • 如果函数模板和普通函数都可以实现,优先调用普通函数
  • 可以通过空模板参数列表来强制调用函数模板。(func<>(); 就可以了)
  • 函数模板可以发生重载
  • 如果函数模板可以产生更好的匹配,优先调用函数模板
  • 既然提供了函数模板就不要提供普通函数了,容易产生二义性
模板具有局限性

为了解决模板的局限性,提供了模板的重载,可以为这些特定的类型提供具体化的模板。

class Person {
public:
	int m_Age;
	string m_Name;

	Person(int age, string name) {
		this->m_Age = age;
		this->m_Name = name;
	}
};

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_Age == p2.m_Age) {
		return true;
	}
	else {
		return false;
	}
}


void test() {
	Person p1(10, "Tom");
	Person p2(13, "Jack");

	bool ret = myCompare(p1, p2);

}
  • 利用具体化的模板可以解决自定义类型的通用化
  • 学习模板并不是为了写模板,而是能够在STL运用系统提供的模板

1.3类模板

作用
  • 建立一个通用类,类中的成员、数据类型可以不具体制定,用一个虚拟的类型来表示。
语法

(和函数模板语法一致)

template<typename T>
// 类
类模板和函数模板的区别:
  • 类模板没有自动类型推导的方式
  • 类模板在模板参数列表中可以有默认参数
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 << this->m_Name << ":" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};
void test51() {

	// Person<string, int> p1("张三", 10);
	// 前面设置了:template<class NameType,class AgeType = int>,所以在使用的时候就可以不加int
	Person<string> p1("张三", 10);

	p1.showPerson();

}
类模板中成员函数和普通类中成员函数创建时机
  • 普通类中的成员函数一开始就会创建。
  • 类模板中成员函数在调用的时候才创建。
类模板对象作函数参数

有三种传入方式:

  • 指定传入的类型 – 直接显示对象的数据类型 (最常用)
  • 参数模板化 – 将对象中的参数变为模板进行传递
  • 整个类模板化 – 将这个对象类型模板化进行传递
template<class NameType,class AgeType>
class Person
{
public:
	Person(NameType name, AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson() {
		cout << this->m_Name << ":" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};

// 方法1:指定传入的类型
void printShowPerson(Person<string, int> &p){
	p.showPerson();
}

void test1() {
	Person<string, int> p1("张三", 10);
	printShowPerson(p1);
}

// 方法2:将参数模板化
template<class T1,class T2>
void printShowPerson2(Person<T1, T2> &p){
	p.showPerson();
}

void test2(){
	Person<string, int> p2("张三", 10);
	printShowPerson2(p2);
}

// 方法3:将整个类模板化
template<class T1>
void printShowPerson3(T &p){
	// 查看数据类型
	cout << "T的数据类型为:" << typeid(T).name() << endl;
	p.showPerson();
}

void test2(){
	Person<string, int> p3("张三", 10);
	printShowPerson3(p3);
}
类模板与继承

注意:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需变为类模板
template<class T>
class Base
{
	T m;
};
// class Son: public Base 错误,不知道父类中的T类型
class Son: public Base<int> // 在声明时指定父类中T的类型。
{};
template<class T1, class T2> // 将子类也变换成类模板,可以灵活的指定父类中T的类型
class Son2: public Base<T1>
{
	T1 m1;
	T2 m2;
};

// 调用
void test()
{
	Base<int> b1;
	Son<char> s1;
	Son2<char, bool> s2;
}
类模板成员函数类外实现
  • 注意:类模板成员函数类外实现,需要加上模板参数列表
// 构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){}

// 成员函数类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson(){}
类模板分文件编写

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

  • 方法1:直接包含.cpp源文件
  • 方法2:将声明和实现写在同一个文件中,并更改后缀名为.hpp, .hpp是约定的名称,并非强制的。
// person.h的代码:
#pragma once
#include <iostream>
using namespace std;
#include <string>
template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	showPerson();
};

// person.cpp中的代码:
#include "person.h"

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){}

template<class T1, class T2>
void Person<T1, T2>::showPerson(){}
// person.hpp的代码:
#pragma once
#include <iostream>
using namespace std;
#include <string>
template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	showPerson();	
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){}

template<class T1, class T2>
void Person<T1, T2>::showPerson(){}
// 主文件中的代码:
#include "person.h" // 当仍然和普通的分文件编写一致时,会报错
#include "person.cpp" // 仍然和普通的份文件编写一致,但是导入的是源文件,就不会报错
#include "person.hpp" // 将person.h与person.cpp中的内容写到一起,然后将后缀名改为.hpp

类模板与友元
template<class T1, class T2>
class Person;

template<class T1, class T2>
void printPerson2(Person<T1, T2> &p)
{}

template<class T1, class T2>
class Person
{
	// 全局函数类内实现
	friend void printPerson(Person<string, int> &p){}
	
	// 全局函数类外实现
	// 如果是类外实现,需要让编译器提前知道这个函数的存在
	friend void printPerson2<>(Person<string, int> &p);
	
public:
	Person(T1 name, T2 age){
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};
类模板案例

实现一个通用的数组类,描述:

  • 可以对内置数据类型以及自定义数据类型的数据进行存储。
  • 将数组中的数据存储到堆区
  • 构造函数中可以传入数组的容量
  • 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
  • 提供尾插法和尾删法对数组中的数据进行增加和删除。
  • 可以通过下标的方式访问数组中的元素。
  • 可以获取数组中当前元素个数和数组的容量。
// 自己的通用的数组类
#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(const MyArray& arr) {
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		// 深拷贝
		this->pAddress = new T[this->m_Capacity];
		// 把数据拷贝过来
		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[this->m_Capacity];
		for (int i = 0; i < this->m_Capacity; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}

		return *this;
	}

	// 尾插法
	void Push_Back(const T & value) {
		// 判断还未到达最大容量
		if (this->m_Size <= this->m_Capacity) {
			this->pAddress[this->m_Size++] = value;
		}
	}

	// 尾删法
	void Pop_Back() {
		// 让用户访问不到最后一个元素
		if (this->m_Size != 0) {
			this->m_Size--;
		}
	}

	// 通过下标的方式访问数组中的元素
	T& operator[](int index) {
		return this->pAddress[index];	
	}

	// 返回数组容量
	int getCapacity() {
		return this->m_Capacity;
	}

	// 返回数组大小
	int getSize() {
		return this->m_Size;
	}

	// 析构函数
	~MyArray() {
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
	}

private:
	T* pAddress; // 指针指向堆区开辟的真实数组

	int m_Capacity; // 数组容量

	int m_Size; // 数组大小

};

2.STL

2.1STL

2.1.1 STL基本概念
  • STL(Standard Template Library,标准模板库)
  • STL从广义上分为:容器、算法、迭代器
  • 容器和算法之间通过迭代器进行无缝连接
  • STL几乎所有的代码都采用了模板类或者模板函数
2.1.2STL六大组件

容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  • 容器:各种数据结构,如vector、list、deque、set、map等
  • 算法:各种常用的算法,如sort、find、copy、for_each等
  • 迭代器:容器和算法之间的胶合剂
  • 仿函数:行为类似函数,可作为算法的某种策略
  • 适配器:用来修饰容器、仿函数、迭代器接口的东西
  • 空间适配器:负责空间的配置和管理
2.1.3STL中的容器、算法、迭代器

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

  • 常用的数据结构:数组、链表、树、栈、队列、集合、映射表等。
  • 这些容器分为序列式容器和关联式容器两种:
    • 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
    • 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。

算法: 使用有限步骤解决逻辑(数学)问题

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

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

  • 提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
  • 每个容器都有自己专属的迭代器。
  • 迭代器的使用类似于指针的使用。

迭代器种类:

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

常用的迭代器为双向迭代器和随机访问迭代器

2.2STL-常用容器

2.2.1string容器

本质:

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

string和char*的区别:

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

特点:

  • string类内部封装了很多成员方法
  • string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责。

string构造函数:
构造函数原型:

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

string赋值操作:
赋值的函数原型:

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

string字符串拼接:

描述:实现在字符串末尾拼接字符串
函数原型:

  • string& operator+=(const char* s);
  • string& operator+=(const char c);
  • string& operator+=(const string& s);
  • string& append(const char* s);
  • string& append(const char* s,int n);把字符串s的前n个字符拼接到当前字符串末尾
  • string& append(const string& s);
  • string& append(const string& s,int pos,int n);把字符串s从第pos位置开始的n个字符拼接到当前字符末尾

string查找和替换:
查找:查找指定字符串是否存在
替换:在指定的位置替换字符串
函数原型:(找不到返回-1,找到返回位置)

  • int find(const string& str,int pos = 0) const;查找str第一次出现的位置,从pos开始查找
  • int find(const char* str,int pos = 0) const;同上
  • int find(const char* str,int pos,int n) const;从pos位置开始查找str的前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* str,int pos = npos) const;同上
  • int rfind(const char* str,int pos,int n) const;从pos位置开始查找str前n个字符最后一次出现的位置
  • int rfind(const char c,int pos = 0) const;查找字符c最后一次出现的位置
  • string& replace(int pos,int n,const string& str);将从pos开始的n个字符替换为str
  • string& replace(int pos,int n,const char* str);同上

string字符串比较:
比较方式:从前到后,一个一个的按照字符的ASCII码进行比较

  • 等于:返回 0
  • 大于:返回 1
  • 小于:返回 -1

函数原型:

  • int compare(const string& str) const;
  • int compare(const char* str) const;

string字符存取:
单个字符存取的两种方式:

  • char& operator[](int n);通过[]存取字符
  • char& at(int n);通过at方法获取字符

string插入和删除:
函数原型:

  • string& insert(int pos,const string& str);从pos位置插入字符串
  • string& insert(int pos,const char* str);同上
  • string& insert(int pos,int n,char c);在pos位置插入n个字符c
  • string& erase(int pos,int n = npos);删除从pos开始的n个字符

string子串:
从字符串中获取想要的子串
函数原型:

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

2.2.2vector容器(数组)

功能: vector的数据结构类似于数组,被称为单端数组

vector与普通数组的区别: 数组是静态空间,vector可以动态扩展

动态扩展:

  • 从新找一块更大的内存空间,将原数据拷贝到新空间,释放原空间
  • vector容器的迭代器是支持随机访问的迭代器

vector构造函数:
创建vector容器
函数原型:

  • vector< T> v;采用 模板实现类 实现,默认构造参数
  • vector(v.begin(),v.end());将v.begin()和v.end()区间中的元素拷贝给本身
  • vector(n,elem);构造函数将n个elem拷贝给本身
  • vector(const vector& vec);拷贝构造函数

vector赋值操作:
函数原型:

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

vector容量和大小:
函数原型:

  • empty();判断容器是否为空
  • capacity();容器的容量
  • size();返回容器中元素的个数
  • resize(int num);重新指定容器的长度num,若容器变长,则以默认值填充新位置。若变短,超出部分则删除。
  • resize(int num,elem);若变长,则变长部分以elem填充。

vector插入和删除:
函数原型:

  • push_back(ele);尾部插入元素ele
  • pop_back();删除最后一个元素
  • 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();删除容器中所有元素

vector数据存取:

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

vector互换容器:
函数原型:

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

用处:收缩内存空间。
例如:刚开始一个很大的数组,系统给它分配一片很大的内存空间,使用resize()直接把这个数组减小到很小,但是它的空间还是非常大。

vector<int>(v).swap(v);  // 就可以解决这个问题
// vector<int>(v) 这是个匿名对象,它是通过拷贝构造来的,匿名对象会在本句执行完毕释放
// v 这是原来的对象
// swap 将匿名对象的指向和原来对象的指向交换
// 结果也就是匿名对象指向原来的大空间,被释放,而原来对象指向匿名对象的空间,被保留下来

vector预留空间:
功能:减少vector在动态扩展容量时的扩展次数
函数原型:

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

2.2.3deque容器

功能: 双端数组,可以对头端进行插入删除操作

deque与vector比较:

  • vector对头部插入删除的效率低,数据量越大,效率越低
  • deque相对而言对头部插入删除速度快
  • vector访问元素的速度比deque快

原理: 在内部有中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据。中控器维护每个缓冲区的地址。
在这里插入图片描述
deque的迭代器支持随机访问

deque的构造函数:
函数原型:(都类似于vector)

  • deque< T> deqT;默认构造
  • deque(beg,end);
  • deque(n,elem);
  • deque(const deque& deqT);

deque赋值操作:
函数原型:

  • deque& operator=(const deque& deq);
  • assign(beg,end);
  • assign(n,elem);

deque容器大小操作:
函数原型:(和vector一样)

  • empty();
  • size();
  • resize(num);
  • resize(num,elem);
  • deque容器没有容量的概念

deque插入和删除:
两端插入和删除:

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

在指定位置进行插入和删除操作:

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

deque数据存取:

  • at(int idx);
  • operator[];
  • front();返回容器中第一个数据元素
  • back();最后一个

deque排序操作:
算法:(默认升序)

  • sort(iterator beg,iterator end);对beg和end区间内元素进行排序

2.2.4stack容器(栈容器)

特点: 是一种先进后出的数据结构,它只有一个出口
栈不允许遍历

stack常用接口:
构造函数:

  • stack< T> stk;
  • stack(const stack& stk);拷贝构造函数

赋值操作:

  • stack operator=(const stack& stk);

数据存取:

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

大小操作:

  • empty();判断栈堆是否为空
  • size();返回栈的大小

2.2.5queue容器(队列容器)

特点: 先进先出的数据结构,有一个入口一个出口

queue常用接口:
构造函数:

  • queue< T> que;
  • queue(const queue& que);

赋值操作:

  • queue operator=(const queue& que);

数据存取:

  • push(elem);队尾添加元素
  • pop();对头移出第一个元素
  • back();返回队尾元素
  • front();返回队头元素

大小操作:

  • empty();
  • size();

2.2.6list容器(链表)

功能: 将数据进行链式存储
链表: 逻辑上通过指针连续,物理上非连续
组成: 由一系列结点组成。

  • 结点由数据域和指针域组成
    • 数据域存储此结点的数据
    • 指针域存储下一个节点的地址

不支持随机访问
优点: 可以在链表的任意位置快速的插入或删除数据、动态分配空间
缺点: 遍历慢、空间大

STL中的链表是一个双向循环链表
插入操作和删除操作不会使原有的list迭代器失效

构造函数:

  • list< T> lst;
  • list(beg,end);
  • list(n,elem);将n个elem拷贝给本身
  • list(const list &lst);

赋值和交换:

  • assign(beg,end);类似第二个构造
  • assign(n,elem);
  • list& operator=(const list& lst);
  • swap(lst);将lst与本身的元素互换

大小操作:

  • size();
  • empty();
  • resize(num);重新指定大小
  • resize(num,elem);

插入和删除:

  • push_back(elem);在尾部插入数据
  • push_front(elem);在头部插入数据
  • pop_back();
  • pop_front();
  • insert(pos,elem);pos位置插入elem,返回新数据位置
  • insert(pos,n,elem);pos位置插入n个elem,无返回
  • insert(pos,beg,end);在pos插入[beg,end)中数据,无返回
  • clear();清空
  • erase(beg,end);删除区间的数据,返回下一个数据的位置
  • erase(pos);删除pos位置数据,返回下一个数据的位置
  • remove(elem);删除容器中所有与elem值匹配的元素

数据存取:

  • front();返回第一个元素
  • back();返回最后一个元素

翻转和排序:

  • reverse();翻转链表
  • sort();排序链表,排序规则自己制定规则方法,将它作为参数。
    所有不支持随机访问迭代器的容器,不可以用标准算法,它的内部会提供相应的一些算法

自定义类型排序时一定要自己指定规则。

2.2.7set容器

特点: 元素会在插入时自动排序
本质: set/multiset属于关联式容器,底层结构是用二叉树实现的。

set和multiset的区别:

  • set不允许容器中有重复的元素
  • multiset允许容器中有重复的元素
  • set插入数据的同时会返回插入结果,表示插入是否成功
  • multiset不会检测数据,因此可以插入重复数据

构造函数:

  • set< T> st;
  • set(const set& st);

赋值:

  • set& operator=(const set& st);

大小和交换:

  • size();
  • empty();
  • swap(st);交换两个集合容器

插入和删除:

  • insert(elem);在容器中插入元素
  • clear();
  • erase(pos);删除pos指向元素,返回下一个元素的迭代器
  • erase(beg,end);删除区间内元素,返回下一个元素的迭代器
  • erase(elem);删除容器中值为elem的元素

查找和统计:

  • find(key);查找key是否存在,若存在返回该元素的迭代器。若不存在,返回set.end()。
  • count(key);统计key的元素个数

pair对组创建:

  • pair<type,type> p (value1,value2);
  • pair<type,type> p = make_pair(value1,value2);
  • 访问对组数据
    • p.first:第一个数据
    • p.second:第二个数据

set容器排序:

  • 利用仿函数,改变排序规则
  • 自定义的数据类型都需要指定排序规则
class MyCompare
{
public:
	bool operator()(int v1, int v2){
		return v1 > v2;
	}
};
class ComparePerson
{
public:
	bool operator()(const Person& p1, const Person& p2){
		return p1.m_Age > p2.m_Age;
	}
};
class Person
{
public:
	Person(string name, int age){
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	string m_Name;
	int m_Age;
};

void test1(){
	set<int, MyCompare> s;
	s.insert(10);
	s.insert(40);
	s.insert(70);
	s.insert(30);
	s.insert(20);
	s.insert(60);

	for(set<int, MyCompare>::iterator it = s.begin();it != s.end();it++){
		cout << *it << " ";
	}
	cout << endl;
}

void test2(){
	set<Person, ComparePerson> s;
	s.insert("a",10);
	s.insert("b",40);
	s.insert("c",70);
	s.insert("d",30);
	s.insert("e",20);
	s.insert(60);

	for(set<int, ComparePerson>::iterator it = s.begin();it != s.end();it++){
		cout << *it << " ";
	}
	cout << endl;

}

2.2.8map容器

特点:

  • map中所有元素都是pair
  • pair中第一个元素为key(键值),起到索引作用。第二个元素为value(实值)
  • 所有元素都会根据元素的键值自动排序

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

优点: 可以根据key值快速找到value值

map和multimap的区别:

  • map不允许容器中有重复的key值
  • multimap允许容器中有重复的key值

map构造和赋值:

  • map<T1,T2> m;
  • map(const map &m);
  • map& operator=(const map& m);

大小和交换:

  • size();
  • empty();
  • swap(mp);

插入和删除:

  • insert(elem);
    • insert(pair<T1,T2>(elem1,elem2));
    • insert(make_pair(elem1,elem2));
    • insert(map<T1,T2>::value_type(elem1,elem2));
    • mp[key] = value;
  • clear();
  • erase(pos);
  • erase(beg,end);
  • erase(key);

查找和统计:

  • find(key);查找key是否存在,存在返回该迭代器,不存在返回map.end()迭代器
  • count(key);统计key的个数

排序:

  • 默认按照key值进行从小到大排序
  • 利用仿函数改变排序规则

2.3STL-函数对象

2.3.1函数对象

概念:

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

本质:

  • 函数对象是一个类,不是一个函数

函数对象使用特点:

  • 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
  • 函数对象超出普通函数的概念,函数对象可以有自己的状态,就是它可以有自己的成员属性及方法
  • 函数对象可以作为参数传递

2.3.2谓词

概念:

  • 返回bool类型的仿函数称为谓词。
  • 如果operator()接收一个参数,那么叫做一元谓词
  • 如果operator()接收两个参数,那么叫做二元谓词

2.3.3内建函数对象

概念:

  • STL内建了一些函数对象

分类:

  • 算数仿函数
  • 关系仿函数
  • 逻辑仿函数

用法:

  • 这些仿函数所产生的对象,用法和一般函数完全相同
  • 使用内建函数对象,需要引入头文件#include < functional>
算数仿函数:

功能描述:

  • 实现四则运算
  • 其中negate是一元运算,其他都是二元运算

仿函数原型:

  • template <class,T> T plus< T> 加法仿函数
  • template <class,T> T minus< T> 减法仿函数
  • template <class,T> T multiplies< T> 乘法仿函数
  • template <class,T> T divides< T> 除法仿函数
  • template <class,T> T modulus< T> 取模仿函数
  • template <class,T> T negate< T> 取反仿函数
关系仿函数

功能描述:

  • 实现关系对比

仿函数原型:

  • template <class,T> bool equal_to< T> 等于
  • template <class,T> bool not_equal_to< T> 不等于
  • template <class,T> bool greater< T> 大于
  • template <class,T> bool greater_equal< T> 大于等于
  • template <class,T> bool less< T> 小于
  • template <class,T> bool less_equal< T> 小于等于
逻辑仿函数

功能描述:

  • 实现逻辑运算

仿函数原型:

  • template <class,T> bool logical_and< T> 逻辑与
  • template <class,T> bool logical_or< T> 逻辑或
  • template <class,T> bool logical_not< T> 逻辑非

2.4STL-常用算法

概述:

  • 算法主要是由头文件< algorithm> < functional> < numeric> 组成
  • < algorithm>是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等
  • < numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数。
  • < functional>定义了一些模板类,用以声明函数对象

2.4.1遍历算法

  • for_each(iterator beg,iterator end,_func); 遍历容器
  • transform(iterator beg1,iterator end1,iterator beg2,_func); 搬运容器到另一个容器中

2.4.2查找算法

  • find(iterator beg,iterator end,value);查找元素, 找不到返回迭代器end()
  • find_if(iterator beg,iterator end,_Pred);按条件查找元素,_Pred:函数或者谓词
  • adjacent_find(iterator beg,iterator end);查找相邻重复元素
  • bool binary_search(iterator beg,iterator end,value);二分查找法,找到返回true,否则返回false,在无序序列中不可使用
  • count(iterator beg,iterator end,value);统计元素个数
  • count_if(iterator beg,iterator end,_Pred);按条件同级元素个数

2.4.3排序算法

  • sort(iterator beg,iterator end,_Pred);对容器内元素进行排序
  • random_shuffle(iterator beg,iterator end);洗牌,指定范围内的元素随机调整次序
  • merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);容器元素合并,并存储到另一容器中,两个容器必须是有序的
  • reverse(iterator beg,iterator end);反转指定范围的元素

2.4.4拷贝和替换算法

  • copy(iterator beg,iterator end,iterator dest);将容器内指定范围的元素拷贝到另一容器中
  • replace(iterator beg,iterator end,oldvalue,newvalue);将指定范围内的旧元素改为新元素
  • replace_if(iterator beg,iterator end,_Pred,newvalue);满足条件的。
  • swap(container c1,container c2);互换两个容器的元素,要交换的容器是同种类型

2.4.5算数生成算法

  • accumulate(iterator beg,iterator end,value);计算容器元素累计总和,参数value是起始的累加值
  • fill(iterator beg,iterator end,value);向容器中填充指定的元素

2.4.6集合算法

  • set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);求两个容器的交集
  • set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);求两个容器的并集
  • set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);求两个容器的差集
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值