C++语法(高级)
模板
C++的另一种编程思想:泛型编程;主要利用的技术就是模板
模板就是建立通用的模具,提高复用性。
模板的特点:
- 模板不可以直接使用,它只是个框架
- 模板的通用并不是万能的
C++提供两种模板机制:函数模板和类模板
函数模板
函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
函数模板基本语法
语法:
template<typename T>
- 函数声明或定义
解释:
- template – 声明创建模板
- typename – 表明其后面的符号是一种数据类型;可以用class代替
- T – 通用的数据类型,名称可以替换,通常为大写字母
使用自动模板的方式:
- 自动类型推导
- 显示指定类型
#include <iostream>
using namespace std;
template<typename T>
void swapData(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
void test01()
{
//1.自动类型推导
int a = 2;
int b = 3;
swapData(a,b);
cout << "a: " << a << endl;
cout << "b: " << b << endl;
}
void test02()
{
//2.显示指定类型
float a = 1.1;
float b = 2.1;
swapData<float>(a, b);
cout << "a: " << a << endl;
cout << "b: " << b << endl;
}
int main()
{
test01();
cout << "--------" << endl;
test02();
}
函数模板注意事项
- 自动类型推导,必须推导出一致的数据类型T,才可以使用
- 模板必须要确定T的类型,才可以使用
#include <iostream>
using namespace std;
template<typename T>
void swapData(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
void test01()
{
int a = 2;
char c = 'c';
swapData(a,c); // 编译器会提示错误,不能推导出一致的数据类型
}
template<typename T>
void func() //此函数没有用到数据类型T
{
cout << "func 调用" << endl;
}
void test02()
{
func(); //编译器会提示错误,没有确定T的类型
func<int>();//此行代码是正确的,因为告诉编译器T是个int(or float/char等)
}
int main()
{
test01();
test02();
}
案例:利用函数模板封装一个排序函数,可以对不同数据类型的数组进行排序;规则:从大到小,排序为选择排序;分别利用char数组和int数组进行测试
#include <iostream>
using namespace std;
template<typename T>
void swapData(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
template<typename T>
void mySort(T &arr)
{
int size = sizeof(arr)/sizeof(arr[0]);
for (int i = 0; i < size-1; i++)
{
int max = i;
for (int j = i+1; j < size; j++)
{
if (arr[i] < arr[j])
{
max = j;
}
}
if (max != i)
{
swapData(arr[max], arr[i]);
}
}
}
template<typename T>
void printArray(T &arr)
{
int num = sizeof(arr)/sizeof(arr[0]);
for (int i = 0; i < num; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
int arr1[] = {2, 3, 6, 1, 7, 8};
mySort(arr1);
printArray(arr1); // 输出结果:8 7 6 2 3 1
}
void test02()
{
char arr2[] = "badcfe";
mySort(arr2);
printArray(arr2); // 输出结果:e b f d c a
}
int main()
{
test01();
test02();
}
普通函数与函数模板的区别
区别:
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换。
- 函数模板调用时,如果利用显示指定类型的方式,可以发生隐式类型转换。
#include <iostream>
using namespace std;
// 普通函数
int myAdd01(int a, int b)
{
return a + b;
}
// 模板函数
template<typename T>
int myAdd02(T a, T b)
{
return a + b;
}
void test01()
{
int a = 10, b = 20;
cout << myAdd01(a, b) << endl; // 输出结果:30; 没有发生隐式类型转换
// 测试 隐式转换
char c = 'c'; // c -> 99
cout << myAdd01(a, c) << endl; // 输出结果:109; 自动将'c'隐式的转换为整型
// 自动类型推导
cout << myAdd02(a, c) << endl; // 编译器会提示报错,因为不会发生隐式类型转换,c是char,a是int,所以会报错
// 显示指定类型
cout << myAdd02<int>(a,c) << endl; // 输出结果:109; 显示将char转换为int
}
int main()
{
test01();
}
普通函数与函数模板调用规则
调用规则如下:
- 如果普通函数和函数模板都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
#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 test01()
{
int a = 10, b = 20;
myPrint(a, b); // 输出:调用的普通函数
// 通过空模板参数列表,强制调用函数模板
myPrint<>(a, b); // 输出:调用的函数模板
// 函数模板重载
myPrint(a, b, 30);
// 如果函数模板可以产生更好的匹配,优先调用函数模板
char c1='c', c2='p';
myPrint(c1, c2); // 输出: 调用的函数模板;如果调用myPrint(int a, int b),需要做隐式类型转换,所以会调用函数模板而不是普通函数
}
int main()
{
test01();
}
模板的局限性
局限性:模板的通用性并不是万能的
例如:
template<typename T>
void func01(T a, T b)
{
a = b;
}
如果上述代码传入的参数a、b是数组,那么赋值操作就无法实现了。
template<typename T>
void func02(T a, T b)
{
if(a > b)
{
/* code */
}
}
如果上述代码传入的参数a、b是自定义数据类型(例如:Person),那么就无法正常运行了。
因为这种问题,C++提供来模板重载,来为特定的类型提供具体化的模板。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
this->m_Age = age;
this->m_Name = name;
}
};
template<typename T>
bool myCompare(T &a, T &b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
// 重载模板,具体化模板
template<> bool myCompare(Person &p1, Person &p2)
{
if (p1.m_Age == p2.m_Age && p1.m_Name == p2.m_Name)
{
return true;
}
else
{
return false;
}
}
void test01()
{
int a=10, b=20;
bool ret = myCompare(a, b);
if (ret)
{
cout << "a == b" << endl;
}
else
{
cout << "a != b" << endl;
}
}
void test02()
{
Person p1("Andy", 18);
Person p2("Andy", 18);
bool res = myCompare(p1, p2);
if (res)
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
}
int main()
{
test01();
test02();
}
总结:
- 利用具体化模板,可以解决自定义类型的通用化
- 学习模板不是为了写模板,而是在STL中能够运用系统提供的模板
类模板
基本语法
类模板作用:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
语法:
template<typename T>
ortemplate<class T>
- 类
Tips: 如果多个参数,可以这样写template<class T1, class T2, ...>
解释:
- template – 声明创建模板
- typename – 表明其后面的符号是一种数据类型;可以用class代替
- T – 通用的数据类型,名称可以替换,通常为大写字母
#include <iostream>
using namespace std;
template<class NameType, class AgeType>
class Person
{
public:
NameType m_Name;
AgeType m_Age;
Person(NameType name, AgeType age)
{
this->m_Age = age;
this->m_Name = name;
}
};
void test01()
{
Person<string, int> p1("Tom", 18);
cout << p1.m_Name << "\t" << p1.m_Age << endl;
}
int main()
{
test01();
}
类模板与函数模板区别
区别:
- 类模板没有自动类型推导的使用方式(C++17中已新增“自动类型推导”功能)
- 类模板在模板参数列表中可以有默认参数
#include <iostream>
using namespace std;
template<class NameType, class AgeType=int> // 模板参数列表有默认参数
class Person
{
public:
NameType m_Name;
AgeType m_Age;
Person(NameType name, AgeType age)
{
this->m_Age = age;
this->m_Name = name;
}
void showPerson()
{
cout << "name: " << this->m_Name << "age: " << this->m_Age << endl;
}
};
void test01()
{
Person<string, int> p1("Tom", 18);
p1.showPerson(); // output: Tom 18
Person p2("Andy", 20); // 没报错,是因为本人使用的是C++17
p2.showPerson(); // output: Andy 20
Person p3("Tom", "eighteen"); // 没报错,是因为本人使用的是C++17
p3.showPerson(); // output: Tom eighteen
Person<string> p4("Andy", 28); // 类模板在模板参数列表中可以有默认参数. 所以此时可以不写“int”
}
int main()
{
test01();
cout << "c++ version: " << __cplusplus << endl; // 输出:201703 -> C++17
}
类模板中成员函数创建时机
与普通类中成员函数创建时机的区别:
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
#include <iostream>
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout << "class Person1 -> func " << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "class Person2 -> func " << endl;
}
};
template<class T>
class MyClass
{
public:
T obj;
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
};
int main()
{
// 0.此时[main函数为空时]编译代码,是可以通过的。因为:类模板中的成员函数在调用时才创建
// 1
MyClass<Person1>m1;
m1.func1();
m1.func2(); // 此行代码编译时会报错:no member named 'showPerson2' in 'Person1'
// 2
MyClass<Person2>m2;
m2.func1(); // 此行代码编译时会报错:no member named 'showPerson1' in 'Person2'
m2.func2();
}
类模板对象做函数参数
类模板实例化出的对象,向函数传参的方式:
- 指定传入的类型:直接显示对象的数据类型
- 参数模板化:将对象中的参数变为模板进行传递
- 整个类模板化:将对象类型模板化进行传递
#include <iostream>
using namespace std;
template<class T1, class T2>
class Person
{
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
}
};
//指定传入的类型
void printPerson1(Person<string, int> &p)
{
p.showPerson();
}
void test01()
{
Person<string, int> p("Tom",18);
printPerson1(p);
}
// 参数模板化
template<class T1, class T2>
void printPerson2(Person<T1, T2> &p)
{
p.showPerson();
cout << "T1的类型为:" << typeid(T1).name() << endl;
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
void test02()
{
Person<string, int> p("Andy",18);
printPerson2(p);
}
// 整个类模板化
template<class T>
void printPerson3(T &p)
{
p.showPerson();
cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03()
{
Person<string, int> p("Tim", 20);
printPerson3(p);
}
int main()
{
test01();
test02();
test03();
}
类模板与继承
类模板遇到继承时:
当子类继承的父类是类模板时,子类在声明的时候,需指定父类中的T的类型;
如果不指定,编译器无法给子类分配内存;
如果想灵活指定出父类中T的类型,子类也需变成类模板。
#include <iostream>
using namespace std;
template<class T>
class Base
{
public:
T m;
};
// class Son:public Base {}; // 错误代码,必须要知道父类中的T类型,才能继承给子类
class Son:public Base<int>
{
};
// 灵活指定父类中T的类型,子类也要变成类模板
template<class T1, class T2>
class Son2:public Base<T2>
{
public:
T1 obj;
Son2()
{
cout << "T1的类型为: " << typeid(T1).name() << endl;
cout << "T2的类型为: " << typeid(T2).name() << endl;
}
};
int main()
{
Son s1;
s1.m = 20;
cout << "s1.m: " << s1.m << endl;
Son2<int, string> s2;
s2.obj = 10;
cout << "s2.obj: " << s2.obj << endl;
cout << typeid(s2.m).name() << endl; // string
s2.m = "hello";
cout << "s2.m: " << s2.m << endl;
}
类模板成员函数类外实现
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class Person
{
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age);
void showPerson();
};
//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
//类模板成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
}
int main()
{
Person p1("Tom",18);
p1.showPerson();
}
类模板分文件编写
类模板分文件编写导致的问题:
类模板中成员函数在调用时才会被创建,所以导致分文件编写时链接不到
解决:
- 方式1:直接包含.cpp文件(声明在.h文件,实现在.cpp文件,主函数文件直接导入.cpp文件)
- 方式2:将声明和实现写在一个文件中,并更改后缀名为.hpp,hpp为约定的名称,并不是强制。
直接包含.cpp文件:
<person.h文件>
#pragma once
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class Person
{
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age);
void showPerson();
};
/*===============================================*/
<person.cpp文件>
#include "person.h"
//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
//类模板成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
}
/*===============================================*/
<分文件编写.cpp>
#include <iostream>
#include <string>
using namespace std;
#include "person.cpp"
int main()
{
Person p1("Andy",18);
p1.showPerson();
}
用hpp后缀 <常用方法>
<person.hpp文件>
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class Person
{
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age);
void showPerson();
};
//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
//类模板成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
}
/*===============================================*/
<分文件编写.cpp>
#include "person.hpp"
int main()
{
Person p1("Tom",18);
p1.showPerson();
}
类模板与友元
全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在
类内实现:
#include <iostream>
using namespace std;
template<class T1, class T2>
class Person
{
//全局函数 类内实现
friend void printPerson(Person<T1, T2> p)
{
cout << "类内实现--> name: " << p.m_Name << "\tage: " << p.m_Age << endl;
}
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
};
int main()
{
// 测试“全局函数类内实现”
Person<string, int> p1("Andy", 18);
printPerson(p1);
}
类外实现:
#include <iostream>
using namespace std;
template<class T1, class T2>
class Person;
//全局函数 类外实现
template<class T1, class T2>
void printPerson2(Person<T1, T2> p)
{
cout << "类外实现--> name: " << p.m_Name << "\tage: " << p.m_Age << endl;
}
template<class T1, class T2>
class Person
{
//全局函数 类外实现 [需要加空模板参数列表]
friend void printPerson2<>(Person<T1, T2> p);
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
};
int main()
{
// 测试“全局函数类外实现“
Person<string, int> p2("Tom", 20);
printPerson2(p2);
}
案例
STL
STL初识
为了建立数据结构和算法的一套标准,诞生了STL
STL基本概念
- STL (Standard Template Library) 标准模板库
- STL从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
- 容器和算法之间通过迭代器连接
- STL几乎所有的代码都采用了模板类或者模板函数
STL六大组件
分别为:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,用来存放数据。例如vector、list、deque、set、map等
- 算法:各种常用的算法,例如sort、find、copy等
- 迭代器:扮演了容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种修饰容器/仿函数/迭代器接口的东西
- 空间配置器:负责空间的配置与管理
STL中容器、算法、迭代器
容器:置物之所也
STL容器就是运用最广泛的一些数据结构表现出来
常用的数据结构:数组、链表、树、栈、队列、集合、映射表等
容器分为序列式容器和关联式容器:
- 序列式容器:强调值的排序,每个元素均有固定的位置
- 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,称之为算法
算法分为:质变算法和非质变算法
质变算法:指运算过程中会改变区间内的元素的内容。例如:拷贝、替换、删除等等
非质变算法:指运算过程中不会改变区间内的元素的内容。例如:查找、遍历、计数等等
迭代器:容器和算法之间的粘合剂
提供一种方法,使之能够依序访问容器中的元素,而又无需暴露该容器内部的实现方式。
每个容器都有自己专属的迭代器。
迭代器的使用类似于指针,初学可以将迭代器理解为指针。
迭代器种类:
种类 | 功能 | 支持的运算 |
---|---|---|
输入迭代器 | 对数据只读访问 | 只读,支持++、==、!= |
输出迭代器 | 对数据只写访问 | 只写,支持++ |
前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
双向迭代器 | 读写操作,并能向前和向后操作 | 读写,支持++、- - |
随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、- -、[n]、-n、<、<=、>、>= |
常用的容器中的迭代器种类:双向迭代器、随机访问迭代器
vector
STL中最常用的容器:vector,可以理解为数组
vector存放内置数据类型
容器:vector
算法:for_each
迭代器:vector<int>::iterator
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void MyPrint(int val)
{
cout << val << endl;
}
void test01()
{
vector<int> v; //创建一个vector容器(数组)
v.push_back(10); //向容器中插入数据
v.push_back(20);
//通过迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin(); // begin是起始迭代器,指向容器中的第一个元素
vector<int>::iterator itEnd = v.end(); // end是结束迭代器,指向容器中最后一个元素的下一个位置
//第一种遍历方式
while (itBegin != itEnd)
{
cout << *itBegin << endl;
itBegin++;
}
//第二种遍历方式
for (itBegin; itBegin != itEnd; itBegin++) // => for(vector<int>::iterator it = v.begin(); it != it.end(); it++)
{
cout << *itBegin << endl;
}
//第三种遍历方式
for_each(v.begin(), v.end(), MyPrint);
}
int main()
{
test01();
}
vector存放自定义数据类型
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
this->m_Age = age;
this->m_Name = name;
}
};
void printPerson(string name, int age)
{
cout << "name: " << name << "\tage: " << age << endl;
}
void test01()
{
vector<Person> v;
Person p1("Tom", 18);
Person p2("Andy", 19);
v.push_back(p1);
v.push_back(p2);
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
// cout << "name:" << it->m_Name << "\tage:" << it->m_Age << endl;
cout << "name: " << (*it).m_Name << "\tage:" << (*it).m_Age << endl;
}
vector<Person>::iterator itBegin = v.begin();
vector<Person>::iterator itEnd = v.end();
while (itBegin != itEnd)
{
cout << "name:" << itBegin->m_Name << "\tage:" << itBegin->m_Age << endl;
itBegin++;
}
}
//放对象指针
void test02()
{
vector<Person*> v;
Person p1("Tom", 18);
Person p2("Andy", 19);
v.push_back(&p1);
v.push_back(&p2);
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "name:" << (*it)->m_Name << "\tage:" << (*it)->m_Age << endl;
}
}
int main()
{
// test01();
test02();
}
vector容器中嵌套容器
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void test01()
{
vector< vector<int> > v;
//创建小容器
vector<int> v1;
vector<int> v2;
//向小容器中添加数据
v1.push_back(10);
v1.push_back(100);
v1.push_back(1000);
v2.push_back(20);
v2.push_back(200);
//向大容器中添加小容器
v.push_back(v1);
v.push_back(v2);
//遍历
for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)
{
for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
{
cout << *vit << " ";
}
cout << endl;
}
}
int main()
{
test01();
}
STL-常用容器
string容器
string基本概念
本质:string是C++风格的字符串,string本质上是一个类
string
和char*
区别:
- string 是一个类,类内部封装了char*,管理这个字符串的是一个char*型的容器
- char* 是一个指针
特点:
string内部封装了很多成员方法,如:find、copy、delete、insert、replace等。
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>
#include <string>
using namespace std;
void test01()
{
// string();
string s1; //默认构造
// string(const char* s);
const char * str1 = "hello world";
string s2(str1);
// string(const string& str); //拷贝构造
const string& str2 = "hello cpp";
string s3(str2); // => string s3(s2);
// string(int n, char c);
int n=5;
char c = 'c';
string s4(n, c);
cout << "s1: " << s1 << "\ts2: " << s2 << "\ts3: " << s3 << "\ts4: " << s4 << endl;
}
int main()
{
test01();
}
string赋值操作
赋值的函数原型:
string& operator=(const char* s);
//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>
#include <string>
using namespace std;
void test01()
{
string str1="hello world";
cout << "str1= " << str1 << endl;
string str2 = str1;
cout << "str2= " << str2 << endl;
string str3;
str3 = 'p';
cout << "str3= " << str3 << endl;
string str4;
str4.assign("hello cpp");
cout << "str4= " << str4 << endl;
string str5;
str5.assign("dkjalf", 3);
cout << "str5= " << str5 << endl;
string str6;
str6.assign(str5);
cout << "str6= " << str6 << endl;
string str7;
str7.assign(5,'p');
cout << "str7= " << str7 << endl;
}
int main()
{
test01();
}
string字符串拼接
函数原型:
string& operator+=(const char* str);
//重载+=操作符string& operator+=(const char c);
//重载+=操作符string& operator+=(const string& s);
//重载+=操作符string& append(const char *s);
//把字符串s连接到当前字符串的结尾string& append(const char *s, int n);
//把字符串s的前n个字符连接到当前字符串的结尾string& append(const string &s);
//同operator+=(const string& s)string& append(const string &s, int pos, int n);
//把字符串s从pos位置开始的n个字符连接到当前字符串的结尾
#include <iostream>
#include <string>
using namespace std;
void test01()
{
string str1="hello";
str1 += " world";
cout << "str1= " << str1 << endl;
string str2="I hate you";
str2 += '!';
cout << "str2= " << str2 << endl;
string str3="say:";
str3 += str1;
cout << "str3= " << str3 << endl;
string str4="我";
str4.append("爱玩");
cout << "str4= " << str4 << endl;
string str5="cpp";
str5.append(" and python", 7);
cout << "str5= " << str5 << endl;
string str6="He loves ";
str6.append(str5);
cout << "str6= " << str6 << endl;
string str7="hello cpp, ";
str7.append("heiwoejos", 2, 3);
cout << "str7= " << str7 << endl;
}
int main()
{
test01();
}
string 字符串查找和替换
函数原型:
int find(const string& str, int pos=0) const;
//从pos开始查找str第一次出现的位置int find(const char* s, int pos=0) const;
//从pos开始查找s第一次出现的位置int find(const char* s, int pos=0, int n) const;
//从pos开始查找s的前n个字符第一次出现的位置int find(const char c, int pos=0) const;
//查找字符c第一次出现的位置int rfind(const string& str, int pos=npos) const;
//从pos开始查找str最后一次出现的位置int rfind(const char* s, int pos=npos) const;
//从pos开始查找s最后一次出现的位置int rfind(const char* s, int pos, int n) const;
//从pos开始查找s的前n个字符最后一次的位置int rfind(const char c, int pos=0) const;
//查找字符c最后一次出现的位置string& replace(int pos, int n, const string& str);
//替换从pos开始n个字符为字符串strstring& replace(int pos, int n, const char* s);
//替换从pos开始n个字符为字符串s
Tips:find 从左往右查找,rfind 从右往左查找
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1="hello world";
cout << "l 第一次出现的位置: " << str1.find("l") << endl;
cout << "l 最后一次出现的位置: " << str1.rfind("l") << endl;
str1.replace(6, 5, "cpp");
cout << "替换之后的str1: " << str1 << endl;
}
string字符串比较
比较方法:字符串比较是按字符的ASCII码进行对比(= 返回0;> 返回1;< 返回-1)
函数原型:
int compare(const string &s) const;
//与字符串s比较int compare(const char *s) const;
//与字符串s比较
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1="hello", str2="hellp";
int ret = str1.compare(str2);
cout << ret << endl;
if (ret < 0)
{
cout << "str1 < str2" << endl;
}
else if (ret > 0)
{
cout << "str1 > str2" << endl;
}
else
{
cout << "str1 == str2" << endl;
}
}
string字符串存取
string中单个字符存取方式:
char& operator[](int n);
//通过[]取字符char& at(int n);
//通过at取字符
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "hello";
cout << "左->右,第1个字符: " << str[1] << endl;
cout << "左->右,第2个字符: " << str.at(2) << endl;
//修改单个字符
str[2] = 'p';
cout << "str:" << str << endl;
str.at(2) = 'q';
cout << "str:" << str << endl;
}
string字符串插入、删除
函数原型:
string& insert(int pos, const char* s);
//插入字符string& insert(int pos, const string& str);
//插入字符string& insert(int pos, int n, char c);
//在pos位置插入n个字符cstring& erase(int pos, int n=npos);
//删除从pos开始的n个字符
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str="hello", ss="99";
str.insert(1, "88");
cout << "1. str: " << str << endl;
str.insert(2, ss);
cout << "2. str: " << str << endl;
str.insert(3, 5, 'c');
cout << "3. str: " << str << endl;
str.erase(2, 5);
cout << "4. str: " << str << endl;
}
string字串获取
函数原型:
string substr(int pos=0, int n=npos) const;
//返回从pos开始的n个字符组成的字符串
int main()
{
string str="hello";
string ss = str.substr(1, 2);
cout << ss << endl;
}
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>
#include <vector>
using namespace std;
void printVector(vector<int> &v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int> v1; //默认构造 无参构造
for (int i = 0; i < 5; i++)
{
v1.push_back(i);
}
printVector(v1);
//通过区间方式进行构造
vector<int> v2(v1.begin(),v1.end());
printVector(v2);
//n个elem方式构造
vector<int> v3(10, 5);
printVector(v3);
//拷贝构造
vector<int> v4(v3);
printVector(v4);
}
int main()
{
test01();
}
vector赋值操作
函数原型:
vector& operator=(const vector &vec);
//重载=操作符assign(begin, end);
//将[begin, end)区间的数据拷贝赋值给本身assign(n, elem);
//将n个elem拷贝赋值给本身
#include <iostream>
#include <vector>
using namespace std;
void printVector(vector<int> &v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test02()
{
vector<int> v1;
for (int i = 0; i < 5; i++)
{
v1.push_back(i);
}
printVector(v1);
//赋值 operator=
vector<int> v2;
v2 = v1;
printVector(v2);
//赋值 assign(begin, end)
vector<int> v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
//赋值 assign(n, elem)
vector<int> v4;
v4.assign(5,8);
printVector(v4);
}
int main()
{
test02();
}
vector容量和大小
函数原型:
empty();
//判断容器是否为空capacity();
//容器的容量size();
//返回容器中元素的个数resize(int num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。resize(int num, elem);
//重新指定容器的长度为num,若容器变长,则以elem的值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。
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 < 9; i++)
{
v1.push_back(i);
}
printVector(v1);
//重新指定大小
v1.resize(15);
printVector(v1);
if (v1.empty())
{
cout << "容器为空" << endl;
}
else
{
cout << "容器的容量: " << v1.capacity() << endl;
cout << "容器的大小: " << v1.size() << endl;
}
}
void test02()
{
vector<int> v1;
for (int i = 0; i < 9; i++)
{
v1.push_back(i);
}
//重新指定大小并且指定填充值
v1.resize(16, 10);
printVector(v1);
}
int main()
{
test01();
test02();
}
vector插入和删除
函数原型:
push_back(elem);
//尾部插入元素elempop_back();
//尾部删除元素insert(const_iterator pos, elem);
//迭代器指向位置pos(处)插入元素eleminsert(const_iterator pos, int count, elem);
//迭代器指向位置pos(处)插入count个元素elemerase(const_iterator pos);
//删除迭代器指向的元素erase(const_iterator start, const_iterator end);
//删除迭代器从start到end之间的元素clear();
//删除容器中所有元素
#include <iostream>
#include <vector>
using namespace std;
void printVector(vector<int> &v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
vector<int> v1;
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
printVector(v1);
//尾删
v1.pop_back();
printVector(v1);
//插入 [头部插入一个值]
v1.insert(v1.begin(), 8);
printVector(v1);
//插入 [头部插入两个值]
v1.insert(v1.begin(), 2, 9);
printVector(v1);
//头删 [删除一个元素]
v1.erase(v1.begin());
printVector(v1);
//头删 [删除多个元素]
v1.erase(v1.begin(), v1.end()); // ==> v1.clear()
printVector(v1);
//清空
v1.push_back(1);
printVector(v1);
v1.clear();
printVector(v1);
}
vector数据存取
函数原型:
at(int idx);
//返回索引idx所指的数据operator[];
//返回索引idx所指的数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
#include <iostream>
#include <vector>
using namespace std;
void printVector(vector<int> &v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
vector<int> v1; //默认构造 无参构造
for (int i = 0; i < 5; i++)
{
v1.push_back(i);
}
//利用[]访问容器中的元素
for (int i = 0; i < v1.size(); i++)
{
cout << "v1[]: " << v1[i] << endl;
}
//利用at访问容器中的元素
for (int i = 0; i < v1.size(); i++)
{
cout << "v1.at(): " << v1.at(i) << endl;
}
//利用front()访问容器中的第一个元素
cout << "the first of elem for v1 is: " << v1.front() << endl;
//利用back()访问容器中的最后一个元素
cout << "the last of elem for v1 is: " << v1.back() << endl;
}
vector互换容器
功能描述:实现两个容器内元素进行互换
函数原型:swap(vec);
//将vec与本身的元素互换
#include <iostream>
#include <vector>
using namespace std;
void printVector(vector<int> &v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
vector<int> v1;
v1.push_back(5);
v1.push_back(6);
// printVector(v1);
vector<int> v2;
v2.push_back(7);
v2.push_back(8);
// printVector(v2);
v2.swap(v1);
printVector(v1);
printVector(v2);
}
vector预留空间
功能描述:减少vector在动态扩展容量时的扩展次数
函数原型:reserve(int len);
//容器预留len个元素长度,预留位置不初始化,元素不可访问。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.reserve(100);
}
deque容器
deque基本概念
功能:双端数组,头尾皆可插入、删除
deque与vector的区别:
- vector对于头部的插入、删除操作,效率低;且数据越大效率越低
- deque对头部的插入、删除操作比vector快
- vector访问元素比deque快,这和两者的内部实现有关系
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区的内容,缓冲区中存放真实数据,中控器维护的是每段缓冲区的地址,使得使用deque时像一片连续的内存空间。
deque容器的迭代器也支持随机访问。
deque构造函数
函数原型:
deque<T> deqT;
//默认构造形式deque(begin, end);
//构造函数将[begin, end)区间的元素拷贝给本身deque(n, elem);
//构造函数将n个elem元素拷贝给本身deque(const deque &deq);
//拷贝构造函数
#include <iostream>
#include <deque>
using namespace std;
void printDeque(deque<int> &d)
{
// for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
deque<int> d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
deque<int> d2(d1.begin(), d1.end());
printDeque(d2);
deque<int> d3(10, 8);
printDeque(d3);
deque<int> d4(d3);
printDeque(d4);
}
int main()
{
test01();
}
deque赋值操作
函数原型:
deque& operator=(const deque &deq);
//重载=操作符 [deque2 = deque1;]assign(begin, end);
//将[begin, end)区间的数据拷贝赋值给本身 [d1.assign(d2.begin(), d2.end());]assign(n, elem);
//将n个elem赋值给本身 [d2.assign(8, 10);]
deque大小操作
函数原型:
deque.empty();
//判断deque是否为空deque.size();
//返回deque容器中元素的个数deque.resize(num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。deque.resize(num, elem);
//重新指定容器的长度为num,若容器变长,则以elem的值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。
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, begin, end);
//在pos位置插入[begin, end)区间的数据,无返回值clear();
//清空容器中的所有数据erase(begin, end);
//删除[begin,end)区间的数据,返回下一个数据的位置。erase(pos);
//删除pos位置的数据,返回下一个数据的位置。
deque数据存取
函数原型:
at(int idx);
//返回索引idx所指的数据operator[];
//返回索引idx所指的数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
deque排序
功能描述:利用算法实现对deque容器进行排序
算法:sort(iterator begin, iterator end);
//对begin和end区间内的元素进行排序。
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
void printDeque(deque<int> &deq)
{
for (deque<int>::const_iterator it = deq.begin(); it != deq.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
deque<int> d1;
d1.push_back(10);
d1.push_back(8);
d1.push_back(15);
d1.push_back(30);
d1.push_back(9);
sort(d1.begin(), d1.end());
printDeque(d1);
}
int main()
{
test01();
}
stack容器
stack基本概念
概念:stack是一种先进后出(FILO)的数据结构,它只有一个出口。“栈”
只有栈顶元素可以被外界使用,所以栈不能被遍历。
添加元素叫入栈:push
删除元素叫出栈:pop
stack常用接口
构造函数:
stack<T> stk;
//stack采用模板类实现,stack对象的默认构造形式stack(const stack &stk);
//拷贝构造函数
赋值操作:stack &operator=(const stack &stk);
//重载等号操作符
数据存取:
push(elem);
// 向栈顶添加元素pop();
//从栈顶移除第一个元素top();
//返回栈顶元素
大小操作
empty();
//判断堆栈是否为空size();
//返回栈的大小
queue容器
queue基本概念
概念:queue是一种**先进先出(FIFO)**的数据结构,它有两个出口。
队列中只有队头和队尾可以被外界使用,所以队列不能被遍历。
队列中进数据称为:入队 push
队列中出数据称为:出队 pop
queue常用接口
构造函数:
queue<T> que;
//queue采用模板类实现,queue对象的默认构造形式queue(const queue &que);
//拷贝构造函数
赋值操作:queue &operator=(const queue &que);
//重载等号操作符
数据存取:
push(elem);
//从队尾添加元素pop();
//从队头移除第一个元素back();
//返回最后一个元素front();
//返回第一个元素
大小操作:empty();
//判断队列是否为空size();
//返回队列的大小
list容器
list基本概念
功能:将数据进行链式存储
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是一个双向循环链表
链表list中的迭代器只支持前移和后移,属于双向迭代器
链表list的优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
链表list的缺点:
- 所占空间比数组大(需要存储数据域和指针域)
- 遍历时比数组耗时
list有一个重要的性质:插入、删除操作都不会造成原有list迭代器的失效。这在vector里是不成立的。
list构造函数
功能:创建list容器
函数原型:
list<T> lst;
//list采用模板类实现对象的默认构造形式list(begin, end);
//构造函数将[begin,end)区间中的元素拷贝给本身list(n, elem);
// 构造函数将n个elem拷贝给本身list(const list &lst);
//拷贝构造函数
#include <iostream>
#include <list>
int main()
{
list<int> lst; //默认构造
lst.push_back(10);
lst.push_back(30);
lst.push_back(20);
printList(lst);
list<int> lst1(lst.begin(), lst.end()); //区间构造
printList(lst1);
list<int> lst2(lst); //拷贝构造
printList(lst2);
list<int> lst3(10, 8); //list(n,elem)
printList(lst3);
}
list赋值和交换
功能:给list容器赋值、交换list容器
函数原型:
assign(beg, end);
//将[beg,end)区间中的数据拷贝赋值给本身assign(n, elem);
//将n个elem拷贝赋值给本身list &operator=(const list &lst);
//重载等号操作符swap(lst);
//将lst与本身的元素互换
list大小操作
函数原型:
size();
//返回list中元素的个数empty();
//判断list是否为空resize(num);
//重新指定list的长度为num,若list变长,则以默认值填充新位置;若list变短,则末尾超出list长度的元素被删除resize(num, elem);
//重新指定list的长度为num,若list变长,则以elem值填充新位置;若list变短,则末尾超出list长度的元素被删除
list插入和删除
函数原型:
push_back(elem);
//在list尾部加入elem元素pop_back();
//删除list中最后一个元素push_front(elem);
//从list开头插入elem元素pop_front();
//从list开头移除第一个元素insert(pos, elem);
//在pos位置插入elem元素的拷贝,返回新数据的位置insert(pos, n, elem);
//在pos位置插入n个elem数据,无返回值insert(pos,beg,end);
//在pos位置插入[beg,end)区间的数据,无返回值clear();
//清空list中的所有数据erase(beg, end);
//删除[beg,end)区间的数据,返回下一个数据的位置erase(pos);
//删除pos位置的数据,返回下一个数据的位置remove(elem);
//删除list中所有与elem值匹配的元素
list数据存取
函数原型:
front();
//返回第一个元素back();
//返回最后一个元素
list反转和排序
功能描述:将list容器中的元素反转,以及将list容器中的数据进行排序
函数原型:
reverse();
//反转链表listsort();
//链表list排序
set/multiset容器
set基本概念
所有元素都会在插入时自动被排序
本质:set/multiset属于关联式容器,底层结构是用二叉树实现。
set和multiset区别:
- set不允许容器中有重复的元素
- multiset允许容器中有重复的元素
set构造和赋值
构造:
set<T> st;
//默认构造函数set(const set &st);
//拷贝构造函数
赋值:
set &operator=(const set &st);
//重载等号操作符
set大小和交换
函数原型:
size();
//返回set容器中元素的数目empty();
//判断set容器是否为空swap(st);
//交换两个set容器
set插入和删除
函数原型:
insert(elem);
//在set容器中插入elem数据clear();
//清除所有元素erase(pos);
//删除pos位置的元素,返回下一个元素的迭代器erase(beg, end);
//删除区间[beg,end)的所有元素,返回下一个元素的迭代器erase(elem);
//删除set容器中值为elem的元素
set查找和统计
函数原型:
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end()count(key);
//统计key的元素个数 (对于set而言,结果为0或1)
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<int> s;
s.insert(10);
s.insert(20);
s.insert(30);
s.insert(40);
s.insert(40);
set<int>::iterator pos = s.find(20);
if (pos != s.end())
{
cout << "Find it. Value is " << *pos << endl;
}
int num = s.count(40);
cout << "count(40): " << num << endl; //输出:1 对于set而言统计的结果只有0和1两种
}
set和multiset区别
- set不可以插入重复的数据,而multiset可以
- set插入数据的同时会返回插入结果,表示插入是否成功
- multiset不会检测数据,因此可以插入重复数据
#include <iostream>
#include <set>
using namespace std;
void printSet(set<int> &s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void printMultiset(multiset<int>& ms)
{
for (set<int>::iterator it = ms.begin(); it != ms.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
set<int> s;
s.insert(10);
pair<set<int>::iterator, bool> ret = s.insert(20);
if (ret.second)
{
cout << "插入成功" << endl;
}
pair<set<int>::iterator, bool> ret2 = s.insert(20);
if (ret2.second)
{
cout << "插入成功" << endl;
}
else
{
cout << "插入失败" << endl;
}
printSet(s);
multiset<int> ms;
ms.insert(10);
ms.insert(10);
ms.insert(20);
printMultiset(ms);
}
pair对组创建
功能描述:成对出现的数据,利用对组可以返回两个数据
两种创建方式:
pair<type, type> p(value1, value2);
pair<type, type> p = make_pair(value1, value2);
set容器排序
set容器默认排序规则为从小到大。
利用仿函数,可以改变set的排序规则
set存放内置数据类型 - 改变排序
#include <iostream>
#include <set>
//仿函数
class MyCompare
{
public:
bool operator()(int v1, int v2) const
{
return v1 > v2;
}
};
int main()
{
set<int> s1;
s1.insert(10);
s1.insert(50);
s1.insert(30);
s1.insert(90);
s1.insert(70);
printSet(s1);
set<int, MyCompare> s2;
s2.insert(10);
s2.insert(50);
s2.insert(30);
s2.insert(90);
s2.insert(70);
for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++)
{
std::cout << *it << " ";
}
std::cout << endl;
}
set存放自定义数据类型 - 改变排序
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator<(const Person p) const
{
return this->m_Age < p.m_Age;
}
};
//仿函数
class MyCompare2
{
public:
bool operator()(const Person &p1, const Person &p2) const
{
return p1.m_Age > p2.m_Age;
}
};
int main()
{
set<Person> sp;
Person p1("Tom", 18);
Person p2("Andy", 28);
Person p3("Sunny", 22);
sp.insert(p1);
sp.insert(p2);
sp.insert(p3);
for (set<Person>::iterator it = sp.begin(); it != sp.end(); it++)
{
std::cout << (*it).m_Name << " " << it->m_Age << "\t";
}
std::cout << endl;
set<Person, MyCompare2> sp2;
sp2.insert(p1);
sp2.insert(p2);
sp2.insert(p3);
for (set<Person, MyCompare2>::iterator it = sp2.begin(); it != sp2.end(); it++)
{
std::cout << (*it).m_Name << " " << (*it).m_Age << "\t";
}
std::cout << endl;
}
map/multimap容器
map基本概念
- map中所有元素都是pair
- pair中第一个元素为key(键值),起索引作用;第二个元素为value(实值)
- 所有元素都会根据元素的键值自动排序
本质:map/multimap属于关联式容器,底层结构是用二叉树实现。
优点: 可以根据key值快速找到value值
map和multimap的区别:
- map不允许容器中有重复key值元素
- multimap允许容器中有重复key值元素
map构造和赋值
构造:
map<T1, T2> mp;
//map默认构造函数map(const map &mp);
//拷贝构造函数
赋值
map &operator=(const map &mp);
//重载等号操作符
#include <iostream>
#include <map>
using namespace std;
void printMap(map<int, int> &m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key= " << (*it).first << " value= " << it->second << "\t";
}
cout << endl;
}
int main()
{
map<int, int> m;
m.insert(pair<int, int>(1, 20));
m.insert(pair<int, int>(2, 40));
m.insert(pair<int, int>(3, 80));
printMap(m);
map<int, int> m2(m);
printMap(m2);
map<int,int> m3;
m3 = m2;
printMap(m3);
}
map大小和交换
函数原型:
size();
//返回容器中元素的数目empty();
//判断容器是否为空swap(st);
//交换两个map容器
map插入和删除
函数原型:
insert(elem);
//在map容器中插入元素
clear();
//清除所有元素
erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end);
//删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(key);
//删除map中值为key的元素
#include <iostream>
#include <map>
using namespace std;
void test04()
{
map<int, int> m;
m.insert(pair<int, int>(1, 20));
m.insert(make_pair(2, 40));
m.insert(map<int,int>::value_type(3, 80));
m[4] = 50;
printMap(m);
}
map查找和统计
函数原型:
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();count(key);
//统计key的元素个数 【对于map,结果只有0或1;multimap可能大于1】
void test04()
{
map<int, int> m;
m.insert(pair<int, int>(1, 20));
m.insert(make_pair(2, 40));
m.insert(map<int,int>::value_type(3, 80));
m[4] = 50;
m[5] = 50;
printMap(m);
//查找
map<int, int>::iterator pos = m.find(2);
if (pos != m.end())
{
cout << "Find it. key= " << (*pos).first << " value= " << (*pos).second << endl;
}
else
{
cout << "Not find." << endl;
}
//统计
cout << m.count(4) << endl; //结果只有0或1
}
map容器排序
map容器默认排序规则为:按照key值进行从小到大排序
利用仿函数改变排序规则 方法同set排序
STL-函数对象
函数对象
基本概念
- 重载函数调用操作符的类,其对象常称为函数对象;
- 函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质:函数对象(仿函数)是一个类,不是一个函数
基本使用
特点:
- 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
- 函数对象超出普通函数的概念,可以有自己的状态
- 函数对象可以作为参数传递
谓词
概念
- 返回bool类型的仿函数称为谓词
- 如果operator()接受一个参数,叫一元谓词
- 如果operator()接受两个参数,叫二元谓词
一元谓词和二元谓词
#include <iostream>
#include <vector>
#include <algorithm>
//一元谓词
struct GreaterFive
{
bool operator()(int val)
{
return val > 5;
}
}
//二元谓词
class MyCompare()
{
public:
bool operator()(int a, int b)
{
return a > b;
}
}
void test01()
{
vector<int> v;
for(int i=0; i<10;i++)
{
v.push_back(i);
}
vector<int>::iterator it = find_if(v.begin(),v.end(),GreaterFive());
if(it == v.end())
{
cout << "Not find." << endl;
}
else
{
cout << "Find it. value: " << *it << endl;
}
}
void test02()
{
vector<int> v1;
v1.push_back(10);
v1.push_back(80);
v1.push_back(30);
//默认排序 从小到大
sort(v1.begin(),v1.end());
//仿函数改变排序 从大到小
sort(v1.begin(),v1.end(), MyCompare());
}
int main()
{
test01();
test02();
}
内建函数对象(内建仿函数)
内建函数对象意义
分类:算数仿函数、关系仿函数、逻辑仿函数
用法:
- 内建仿函数所产生的对象,用法和一般函数完全相同
- 使用内建函数对象,需要引入头文件
#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>;
//取反仿函数
#include <iostream>
#include <functional>
using namespace std;
int main()
{
//取反仿函数 一元仿函数
negate<int> n;
cout << n(50) << endl; // -50
//加法仿函数
plus<int> p;
cout << p(10, 5) << endl; //15
minus<int> mi;
cout << mi(20, 10) << endl; //10
multiplies<int> mu;
cout << mu(10, 10) << endl; //100
divides<int> d;
cout << d(100, 10) << endl; //10
modulus<int> mod;
cout << mod(12, 10) << endl; //2
}
关系仿函数
仿函数原型:
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>;
//小于等于
#include <iostream>
#include <functional>
using namespace std;
void test01()
{
equal_to<int> e;
cout << e(2, 2) << endl; //1
not_equal_to<int> ne;
cout << ne(1, 2) << endl; //1
greater<int> g;
cout << g(3, 2) << endl; //1
greater_equal<int> ge;
cout << ge(2, 2) << endl; //1
less<int> le;
cout << le(2, 3) << endl; //1
less_equal<int> lq;
cout << lq(2, 2) << endl; //1
}
void test02()
{
vector<int> v1;
v1.push_back(10);
v1.push_back(80);
v1.push_back(30);
//默认排序 从小到大
sort(v1.begin(),v1.end());
//内建仿函数改变排序 从大到小
sort(v1.begin(),v1.end(), greater<int>());
}
int main()
{
test01();
test02();
}
逻辑仿函数
仿函数原型:
template<class T> bool logical_and<T>;
//逻辑与template<class T> bool logical_or<T>;
//逻辑或template<class T> bool logical_not<T>;
//逻辑非
STL-常用算法
概述:
- 算法主要是由头文件
<algorithm>
<functional>
<numeric>
组成 <algorithm>
是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等<numeric>
体积很小,只包括几个在序列上面进行简单数学运算的模板函数<functional>
定义了一些模板类,用以声明函数的对象
常用遍历算法
算法简介:
for_each
//遍历容器transform
//搬运容器到另一个容器中
for_each
功能描述:实现遍历容器
函数原型:for_each(iterator begin, iterator end, _func);
//begin 开始迭代器
//end 结束迭代器
//_func 函数或者函数对象
Tips:==实际开发
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
int main()
{
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(20);
v.push_back(60);
//遍历算法 [普通函数]
for_each(v.begin(), v.end(), print01);
cout << endl;
//遍历算法 [仿函数]
for_each(v.begin(), v.end(), print02());
cout << endl;
}
transform
功能描述:搬运容器到另一个容器中
函数原型:transform(iterator beg1, iterator end1, iterator beg2, _func);
//beg1 源容器开始迭代器
//end1 源容器结束迭代器
//beg2 目标容器开始迭代器
//_func 函数或者函数对象
Tips: transform目标函数需要提前开辟空间,否则会报错
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
void test08()
{
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i + 1);
}
cout << " ==仿函数== " << endl;
vector<int> v2; //目标容器
v2.resize(v1.size()); //目标容器需要提前开辟空间
transform(v1.begin(), v1.end(), v2.begin(), transf2());
for_each(v2.begin(), v2.end(), print02());
cout << endl;
cout << " ==普通函数== " << endl;
vector<int> v3; //目标容器
v3.resize(v1.size()); //目标容器需要提前开辟空间
transform(v1.begin(), v1.end(), v3.begin(), transf1);
for_each(v3.begin(), v3.end(), print01);
}
常用查找算法
算法简介:
find
//查找元素find_if
//按条件查找元素adjacent_find
//查找相邻重复元素binary_search
//二分查找count
//统计元素个数count_if
//按条件统计元素个数
find
功能描述:查找指定元素,找到返回指定元素的迭代器,找不到返回结束迭代器end()。
函数原型:find(iterator beg, iterator end, value);
//beg 开始迭代器
//end 结束迭代器
//value 查找的元素
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
void test01() //内置数据类型
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
vector<string> v2;
v2.push_back("ab");
v2.push_back("ba");
v2.push_back("cd");
v2.push_back("ef");
vector<string>::iterator pos = find(v2.begin(), v2.end(), "cd");
if (pos != v2.end())
{
cout << "Find it, " << *pos << endl;
}
else
{
cout << "Not find." << endl;
}
}
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
//重载==符号;让底层find知道如何对比person数据类型
bool operator==(Person &p) const
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
};
void test02() //自定义数据类型
{
vector<Person> v;
Person p1("Tom", 18), p2("Andy", 20), p3("Sun", 17), p4("Tony", 28);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
Person p5("Cat", 5);
vector<Person>::iterator it = find(v.begin(), v.end(), p3);
if (it != v.end())
{
cout << "Find it. name: " << it->m_Name << ", age : " << it->m_Age << endl;
}
else
{
cout << "Not find." << endl;
}
}
find_if
功能描述:按条件查找元素
函数原型:find_if(iterator beg, iterator end, _Pred);
//按值查找元素,找到 返回指定位置迭代器,找不到 返回结束迭代器位置
//beg 开始迭代器
//end 结束迭代器
//_Pred 函数或者谓词(返回bool类型的仿函数)
Tips:查找到符合条件的第一个元素之后就停止查找,并返回迭代器。
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
//谓词
class geraterFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
class geraterPerson
{
public:
bool operator()(Person &p)
{
return p.m_Age > 18;
}
};
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
//重载==符号;让底层find知道如何对比person数据类型
bool operator==(Person &p) const
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
};
void test01()
{
//内置类型查找
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i + 1);
}
vector<int>::iterator it = find_if(v.begin(), v.end(), geraterFive());
if (it != v.end())
{
cout << *it << endl;
}
//自定义类型查找
vector<Person> v2;
Person p1("Tom", 18), p2("Andy", 20), p3("Sun", 17), p4("Tony", 28);
v2.push_back(p1);
v2.push_back(p2);
v2.push_back(p3);
v2.push_back(p4);
vector<Person>::iterator it_p = find_if(v2.begin(), v2.end(), geraterPerson());
if (it_p != v2.end())
{
cout << "name: " << it_p->m_Name << " age: " << it_p->m_Age << endl;
}
}
abjacent_find
功能描述:查找相邻重复元素
函数原型:adjacent_find(iterator beg, iterator end);
//查找相邻重复元素,返回相邻元素的第一个位置的迭代器
//beg 开始迭代器
//end 结束迭代器
binary_search(二分查找)
功能描述:查找指定元素是否存在
函数原型:bool binary_search(iterator beg, iterator end, value);
只用于有序序列
//查找指定的元素,查到 返回true,否则返回false
//beg 开始迭代器
//end 结束迭代器
//value 查找的元素
count
功能描述:统计元素个数
函数原型:count(iterator beg, iterator end, value);
//统计元素出现次数,返回值类型为int
//beg 开始迭代器
//end 结束迭代器
//value 要统计的元素
Tips:统计自定义数据类型时,需要配合重载operator==
count_if
函数原型:count_if(iterator beg, iterator end, _Pred);
//按条件统计元素出现次数
//beg 开始迭代器
//end 结束迭代器
//_Pred 谓词
#include <iostream>
#include <algorithm>
using namespace std;
//谓词
class greaterFive
{
public:
bool operator()(int val)
{
return val >= 5;
}
};
void test01()
{
//内置数据类型
vector<int> v;
v.push_back(2);
v.push_back(5);
v.push_back(1);
v.push_back(3);
v.push_back(8);
v.push_back(5);
int num = count_if(v.begin(), v.end(), greaterFive());
cout << num << endl;
//自定义数据类型 待补充。。。
}
常用排序算法
算法简介:
sort
//对容器内元素进行排序random_shuffle
//指定范围内的元素随机调整次序merge
//容器元素合并,并存储到另一容器中reverse
//反转指定范围的元素
sort
功能描述:对容器内元素进行排序
函数原型:sort(iterator beg, iterator end, _Pred);
sort(iterator beg, iterator end);
//默认是升序
利用谓词“_Pred”改变排序规则
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
//普通函数
void print01(int val)
{
cout << val << " ";
}
//降序 谓词 仿函数
class Descending
{
public:
bool operator()(int val1, int val2)
{
return val1 > val2;
}
};
void test02()
{
vector<int> v = {2,5,1,3,8,5};
sort(v.begin(), v.end());
for_each(v.begin(), v.end(), print01);
cout << endl;
//sort(v.begin(), v.end(), Descending()); //自定义实现降序
sort(v.begin(), v.end(), greater<int>()); //STL中函数实现降序
for_each(v.begin(), v.end(), print01);
}
random_shuffle (C++11)
函数原型:random_shuffle(iterator beg, iterator end);
//指定范围内的元素随机调整次序
Tips:C++17中没有random_shuffle,用shuffle代替
#include <iostream>
#include <algorithm>
#include <random>
using namespace std;
int main()
{
vector<int> v = {1,3,8,5,9,5};
//创建随机数引擎 [C++17没有random_shuffle函数]
random_device rd;
mt19937 rng(rd());
//使用shuffle打乱容器中的数据
shuffle(v.begin(),v.end(),rng);
for(vector<int>::iterator it = v.begin(); it!= v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
merge
函数原型:merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
//容器元素合并,并存储到另一容器中 【两个容器必须是有序的】
//dest 目标容器开始迭代器
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v2 = { 7,8,9,10 };
vector<int> v1 = { 1,2,3,4,5 };
vector<int> vv;
vv.resize(v1.size() + v2.size());
merge(v2.begin(), v2.end(), v1.begin(), v1.end(), vv.begin());
for_each(vv.begin(), vv.end(), print01);
}
reverse
函数原型:reverse(iterator beg, iterator end);
//反转指定范围内的元素
#include <iostream>
#include <algroithm>
using namespace std;
int main()
{
vector<int> v1 = {1,2,3,4,5,6};
reverse(v1.begin(),v1.end());
for(vector<int>::iterator it=v1.begin(); it!=v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
常用拷贝和替换算法
算法简介:
copy
//容器内指定范围内的元素拷贝到另一容器中replace
//将容器内指定范围的旧元素修改为新元素replace_if
//容器内指定范围满足条件的元素替换为新元素swap
//互换两个容器的元素
copy
函数原型:copy(iterator beg, iterator end, iterator dest);
Tips:提前给目标容器dest 指定大小
replace
函数原型:replace(iterator beg, iterator end, oldvalue, newvalue);
//将区间内旧元素替换成新元素
// oldvalue - 旧元素;newvalue - 新元素
replace_if
函数原型:replace(iterator beg, iterator end, _pred, newvalue);
//满足条件(_pred)的替换成指定元素
//_pred - 谓词;newvalue - 替换的新元素
#include <iostream>
#include <algroithm>
using namespace std;
int main()
{
vector<int> v1 = { 1,2,3,4,5,6 };
replace_if(v1.begin(), v1.end(), greaterFour(), 8);
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
swap
函数原型:swap(container c1, container c2);
// c1、c2必须是同类型的容器
#include <iostream>
#include <algroithm>
using namespace std;
int main()
{
vector<int> v2 = { 7,8,9,10 };
vector<int> v1 = { 1,2,3,4,5,6 };
std::swap(v1, v2);
/*结果:
v1: 7,8,9,10
v2: 1,2,3,4,5,6
*/
}
常用算数生成算法
算数生成算法属于小型算法,使用时包含的头文件为#include <numeric
accumulate
函数原型:accumulate(iterator beg, iterator end, value);
//计算区间内 容器元素累计总和
//value 起始值
#include <iostream>
#include <numeric>
using namespace std;
int main()
{
vector<int> v1 = { 1,2,3,4,5,6 };
int num = accumulate(v1.begin(), v1.end(), 0);
cout << num << endl; // 21
fill
函数原型:fill(iterator beg, iterator end, value);
//向容器中填充指定元素value
#include <iostream>
#include <numeric>
#include <math.h>
using namespace std;
int main()
{
vector<int> v1;
v1.resize(10);
fill(v1.begin(), v1.end(), 8);
/*结果:8 8 8 8 8 8 8 8 8 8 */
}
常用集合算法
set_intersection
函数原型:set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
//求两个集合的交集 [两个集合必须是有序序列];返回dest的最后一个元素的迭代器地址
//dest 目标容器的开始迭代器
#include <iostream>
#include <numeric>
#include <math.h>
using namespace std;
void test01()
{
vector<int> v1 = { 1,2,3,4,5,6 };
vector<int> v2 = { 5,6,7,8,9,10 };
vector<int> v3;
//取两者较小者作为目标容器的大小
v3.resize(min(v1.size(), v2.size()));
//返回目标容器的最后一个元素的迭代器地址
vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
//for_each(v3.begin(), itEnd, print01);
for(vector<int>::iterator it=v3.begin(); it!=itEnd; it++)
{
cout << *it << " ";
}
cout << endl;
}
set_union
函数原型:set_union(iterator beg1,iterator end1,iterator beg2, iterator end2,iterator dest);
//求两个集合的并集 [两个集合必须是有序序列];返回dest的最后一个元素的迭代器地址
//dest 目标容器的开始迭代器
#include <iostream>
#include <numeric>
#include <math.h>
using namespace std;
int main()
{
vector<int> v4;
v4.resize(v1.size() + v2.size());
vector<int>::iterator it_end = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v4.begin());
for(vector<int>::iterator it=v4.begin(); it!=itEnd; it++)
{
cout << *it << " ";
}
cout << endl;
}
set_difference
函数原型:set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2, iterator dest);
//求两个集合的差集 [两个集合必须是有序序列];返回dest的最后一个元素的迭代器地址
#include <iostream>
#include <algorithm>
#include <numeric>
#include <math.h>
using namespace std;
//普通函数
void print01(int val)
{
cout << val << " ";
}
int main()
{
vector<int> v1 = { 1,2,3,4,5,6 };
vector<int> v2 = { 5,6,7,8,9,10 };
vector<int> v5;
v5.resize(max(v1.size(),v2.size())); //取两者较大者作为dest的空间
vector<int>::iterator it_e = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v5.begin());
cout << "v1和v2的差集:" << endl;
for_each(v5.begin(), it_e, print01);
cout << endl;
vector<int> v6;
v6.resize(max(v1.size(), v2.size()));
it_e = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v6.begin());
cout << "v2和v1的差集:" << endl;
for_each(v6.begin(), it_e, print01);
}
/*输出:
v1和v2的差集:
1 2 3 4
v2和v1的差集:
7 8 9 10
*/