【C++ | 基础】进阶编程

C++重点内容

文章目录

进阶编程

1. 模板

针对C++泛型编程STL技术

1.1 模板的概念

通用的模具,提高复用性,将类型参数化

C++另一种编程思想就是泛型编程,主要技术就是模板

模板机制:类模板函数模板

1.2 函数模板

语法:template<typename T> 函数声明或定义typename表明其后面的符号是一种数据类型,可用class代替;T为通用的数据类型

作用:建立一个通用函数,返回值和类型不具体指定,用一个虚拟的类型T代表

使用:

  • ①自动类型推导:func(a,b);直接传入参数,编译器会自动推导传入参数的数据类型
  • ②显式指定类型:func<数据类型>(a,b);显式告诉编译器T的类型

tips

  • 自动类型推导,必须推导出一致的数据类型T才能使用
  • 模板必须要确定出T的数据类型才能使用
1.2.1 普通函数与函数模板的区别

普通函数调用时可以发生自动类型转换(隐式)

函数模板调用时,如果利用自动类型推导不会发生隐式类型转换、利用显式指定类型可以发生隐式类型转换

模板函数建议使用显示指定类型

int func1(int a, int b)//普通函数
{
    return a + b;
}

template<class T>
T func2(T a, T b)//模板函数
{
    retrun a + b;
}

void test()
{
    int a = 1;
    int b = 2;
    int c = 'd';
    func1(a+b);//3,参数都为int,正常调用
    func2(a+b);//3,参数类型一致,可以推导出T为int类型
    func1(a+c);//100,函数会将c的char类型自动转换为int类型
    func2(a+c);//报错,模板函数利用自动类型推导时不会发生隐式类型转换
    func2<int>(a+c);//100,模板函数利用显式指定类型时可以发生隐式类型转换
}
1.2.2 普通函数与函数模板的调用规则

当函数模板和普通函数都可以实现时,优先调用普通函数

可以通过空模板参数列表强制调用模板函数

函数模板也可以重载

如果函数模板能产生更好的匹配,则调动函数模板

提供函数模板后最好不要提供普通函数,容易存在二义性

void func(int a, int b)
{
    cout<<"normal"<<endl;
}

template<class T>
void func(T a, T b)
{
    cout<<"template1"<<endl;
}

template<class T>
void func(T a, T b, T c)
{
    cout<<"template2"<<endl;
}

void test()
{
    int a = 1;
    int b = 2;
    int c = 3;
    func(a,b);//normal,调用的是普通函数
    func<>(a,b);//template1,利用空模板强制调用模板函数
    func(a,b,c);//template2,函数模板发生重载,调用对应版本的函数
    char c1 = 'a';
    char c2 = 'b';
    func(c1,c2);//template1,调用的是模板函数,因为不会发生隐式类型转换,更为简单
}
1.2.3 模板的局限性

有些特定的数据类型需要提供具体化的模板做特殊实现,如数组的赋值操作自定义数据类型的大小比较

template<class T>
bool eql(T &a, T &b)
{
    if(a == b)
    {
        return true;
    }
    return false;
}

template<> bool eql(Person &a, Person &b)//重载模板函数
{
    if(a.name == b.name)
    {
        return true;
    }
    return false;
}

1.3 类模板

语法:template<class Type1, class Type2...> 类模板类中可能有多种数据类型,所以可以在定义模板类时用多种通用类型来代替

1.3.1 类模板和函数模板区别

类模板没有自动类型推导的使用方式

类模板在模板参数列表中可以有默认参数

template<class T = string, class E = int>//类模板可以有默认参数
class Person
{
public:
    Person(T a, E b)
    {
        this->a = a;
        this->b = b;
    }
    T a;
    E b;
};

void test()
{
    Person p("Tom", 10);//错误,类模板不能自动推导类型
    Person<string, int>p("Tom", 10);//正确,必须使用显式指定类型
    Person<string>p("Tom", 10);//正确,模板参数有默认类型
    Person<>p("Tom", 10);//正确,甚至可以让所有模板参数都具有默认类型
}
1.3.2 类模板中成员函数创建时机

普通类中的成员函数一开始就可以创建

类模板中的成员函数在调用时才创建,调用时才根据对象数据类型来判断是否能调用此成员函数

1.3.3 类模板对象做函数参数

类模板实例化出的对象,向函数传参:

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

void func1(Person<string, int> &p)//传参时直接指定传入类型
{
    return;
}

template<class T1, class T2>
void func2(Person<T1, T2> &p)//将对象的模板参数也进行模板化操作
{
    return;
}

template<class T>
void func3(T &p)//将对象的整个类进行模板化
{
    return;
}

void test()
{
    Person<string, int>p("Tom",10);
    func1(p);
    func2(p);
    func3(p);
}
1.3.4 类模板与继承

类模板遇到继承需要注意:

  • 子类继承的父类是一个类模板时,子类在声明的时候,要指出父类中T的类型。如果不指出,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需变为类模板
template<class T>
class Base
{
    T a;
};

class Son : public Base<int>//必须指出T的具体类型
{
    int b;
};

template<class T1, class T2>
class Son2 : public Base<T2>//此时子类也是一个类模板
{
    T1 c;
};
1.3.5 类模板成员函数类外实现

重点是在函数前面声明函数模板,加上类作用域,并在类名后加上模板的参数列表

语法template<class T1, class T2,...> 返回值 类名<T1,T2,...>::func(args){}

template<class T1, class T2>
class Person
{
public:
    T1 name;
    T2 age;
    Person(T1 name, T2 age);
    void func();
};

//构造函数类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->name = name;
    this->age = age;
}

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

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

解决方法:①直接包含.cpp源文件

#include"person.cpp"//直接包含源文件,源文件中包含头文件#include"person.h"

②将声明和实现写到同一个文件中,后缀改为.hpp

#include"person.hpp"//包含.hpp即可,hpp文件中包含头文件和源文件的所有内容
1.3.7 类模板与友元

全局函数类内实现:直接在类内声明友元即可

全局函数类外实现:需要提前让编译器知道全局函数的存在

//类外实现 因为函数定义在类之前,并且使用了类,所以需要在函数定义之前先声明该类的存在
template<class T1, class T2>
class Person;

//类外实现 由于需要让编译器提前知道该函数,所以需要写在类定义的前面,声明该函数的存在
template<class T1, class T2>
void print2<>(Person<T1,T2> p)
{
    cout<<p.a<<p.b<<endl;
}

template<class T1, class T2>
class Person
{
    //全局函数 类内实现
    friend void print1(Person<T1,T2> p)
    {
        cout<<p.a<<p.b<<endl;
    }
    //全局函数 类外实现
    //需要在函数名后加上空模板参数列表,即<>
    friend void print2<>(Person<T1,T2> p)
public:
    Person(T1 a, T2 b)
    {
        this->a = a;
        this->b = b;
    }
private:
    T1 a;
    T2 b;
};

2. STL初识

2.1 STL基本概念

定义:标准模板库

广义上分为:容器算法迭代器

容器和算法之间通过迭代器进行无缝连接

STL几乎所有的代码都采用了模板技术

STL六大组件:

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

2.2 STL中容器、算法、迭代器简介

2.2.1 容器

STL容器:就是将运用最广泛的一些数据结构实现出来,如数组、链表、树、栈、队列、集合、映射等

分类:序列式容器关联式容器

序列式容器:强调值的排序,序列式容器中每个元素都有固定的位置

关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

2.2.2 算法

STL算法:有限的步骤,解决逻辑或数学上的问题

分类:质变算法非质变算法

质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝、替换、删除等

非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等

2.2.3 迭代器

STL迭代器:容器和算法之间粘合剂,算法要通过迭代器才能访问容器中的数据

提供一种方法,使之能够依序寻访某个容器中所含的各个元素,而又无需暴露该容器的内部表示方式

每个容器都有自己专属的迭代器

迭代器使用类似于指针

种类:

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

常用的容器中迭代器种类为双向迭代器和随机访问迭代器

2.3 容器算法迭代器初识

最常用的容器为vector,可以理解为数组

2.3.1 vector存放内置数据类型

容器:vector,需要导入头文件#include<vector>

算法:for_each,需要导入头文件#include<algorithm>

迭代器:vector<int>::iterator,可使用*迭代器名来取其指向的值

**函数指针:**指向函数的指针变量,可用于调用函数、传递参数;声明返回值 (* 指针变量名)(参数类型列表)。调用指针变量名(实参列表)

回调函数:函数指针作为某个函数的参数,回调函数就通过函数指针调用的函数。简单讲就是由别人的函数执行时调用你实现的函数

#include<alogrithm>
#include<vector>

void print(int var)
{
    cout<<var<<endl;
}

int main()
{
    vector<int> a ={0 ,1 , 2, 3};
    for_each(a.begin(),a.end(),print);
}

存放自定义数据类型时同理

3. STL常用容器

3.1 string容器

string本质是一个类,表示C++中的字符串

string和char *的区别:char *是一个指针;string是一个类,但类内部封装了char *,本质和char *一样

string管理char *所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

3.1.1 string构造函数

原型:

  • string():无参构造,创建一个空字符串
  • string(const char* s):有参构造,使用字符串s初始化
  • string(const string &str):使用一个string对象初始化
  • string(int n, char c):使用n个字符c初始化
3.1.2 string赋值操作

原型:

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

实现在字符串末尾拼接字符串

原型:

  • string &operator+=(const char* str)
  • 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个字符添加到当前字符串末尾
3.1.4 string查找和替换

查找指定字符串是否存在

在指定的位置替换字符串

原型:

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

rfind从右往左查找,find从左往右查找

英文字符占用1个字节,中文字符占用2个字节

3.1.5 字符串比较

字符串比较是按字符从ASCII码进行对比。返回0表示=;1表示>;-1表示<

原型:

  • int compare(const string &s) const
  • int compare(const char* s) const
3.1.6 string字符存取

原型:

  • char &operator[](int n):通过[]方式取字符
  • char &at(int n):通过at方式获取字符

两种方法都可以对单个字符进行读写操作

3.1.7 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个字符
3.1.8 string子串

原型:

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

3.2 vector容器

vector数据结构和数组非常相似,称为单端数组

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

**动态扩展:**并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝至新空间中,释放原空间

vector容器的迭代器是支持随机访问的迭代器

3.2.1 vector构造函数

原型:

  • vector<T> v:采用模板类实现,默认构造函数
  • vector(v.begin(),v.end()):将v[begin(),end()]区间内的元素拷贝给当前向量
  • vector(n,elem):构造函数将n个elem拷贝给自身
  • vector(const vector &vec):拷贝构造函数
3.2.2 vector赋值操作

原型:

  • vector &operator=(const vector &vec):重载等号运算符
  • assign(beg, end):将[beg, end)区间中的数据拷贝赋值给当前向量
  • assign(n, elem):将n个elem拷贝赋值给当前向量
3.2.3 vector容量和大小

原型:

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

原型:

  • push_back(elem):尾部插入元素elem
  • pop_back():删除并返回最后一个元素
  • insert(const_iterator pos, elem):迭代器指向位置pos插入elem
  • insert(const_iterator pos, int count , elem):迭代器指向位置pos插入count个elem
  • erase(const_iterator pos):删除迭代器指向的元素
  • erase(const_iterator start, const_iterator end):删除迭代器从start到end之间的元素
  • clear():删除容器中所有元素
3.2.5 vector数据存取

原型:

  • at(int idx):返回索引idx指向的数据
  • operator[]:通过已经封装好的重载运算符[]返回索引idx所指的数据
  • front():返回容器中的第一个元素
  • back():返回容器中的最后一个元素
3.2.6 vector互换容器

原型:

  • swap(vec):将vec和本身的元素互换

利用swap收缩空间:vector<int>(vec).swap(vec)vector<int>(vec)表示的是匿名对象,是调用拷贝构造函数,利用vec构造的一个匿名对象,匿名对象的size()和capacity()和vec的size()相同。然后调用swap函数相当于实现了一个容器互换,将匿名对象的容器和vec的容器进行互换,最后匿名对象在的当前行执行完毕后由系统自动回收,从而实现空间收缩

3.2.7 预留空间

原型:

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

当vector中需要存的数据量特别大时,在元素插入过程中vector会不断因为capacity不够大而寻找新的更大的内存空间。此时可以调用reserve函数使vector一开始就寻找到一块足够大的内存,从而避免重复扩容

3.3 deque容器

3.3.1 deque基本概念

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

deque和vector区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对而言,对头部的插入和删除比vector快
  • vector访问元素时的速度会比deque快,这和两者内部实现有关

数据结构:

image-20210412110410383

内部原理:内部有一个中控器,维护每段缓冲区的内容,缓冲区中存放真实数据。中控器维护的是每个缓冲区的地址,使得使用deque时像一个连续的内存空间。因为deque访问元素时可能需要通过中控器寻找下一块缓冲区的地址,所以deque访问速度没有vector快

deque的迭代器支持随机访问

3.3.2 deque构造函数

原型:

  • deque<T> deq:默认构造
  • deque(beg, end):拷贝beg和end之间的元素
  • deque(n, elem):拷贝n个elem元素
  • deque(const deque &deq):拷贝构造函数
3.3.3 deque赋值操作

原型:

  • deque& operatpr=(const deque &deq):重载=运算符
  • assign(beg,end):拷贝beg到end区间中数据
  • assign(n,elem):拷贝n个elem
3.3.4 deque大小操作

原型:

  • deque.empty():判断容器是否为空
  • deque.size():返回容器中元素的个数
  • deque.resize(n):重新指定容器长度为n,若容器变长,则用默认值填充新位置;若容器变短,则末尾超出容器长度的部分被删除
  • deque.resize(n,elem):用元素elem填充新位置
3.3.5 deque插入删除

原型:

两端插入操作:

  • push_back(elem):在容器尾部添加一个元素elem
  • push_front(elem):在容器头部添加一个元素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位置的元素,返回下一个元素的位置
3.3.6 deque数据存取

原型:

  • at(int idx):返回索引idx指向的元素
  • operator[]:返回索引idx指向的元素
  • front():返回容器中第一个元素
  • back():返回容器中最后一个元素
3.3.7 deque排序

原型:

  • sort(beg,end):对beg和end之间的所有元素进行排序

sort是STL提供了排序算法,使用时需要包括头文件#include<algorithm>,也可以用于vector。对于支持随机访问的迭代器的容器,都可以使用sort算法排序

3.4 stack容器

3.4.1 stack基本概念

stack是一个先进先出的数据结构,只有一个出口

栈只有顶端元素可以被外界使用,不允许遍历

3.4.2 stack接口

原型:

  • stack<T> stk:默认构造
  • stack(const stack &stk):拷贝构造
  • stack& operator=(const stack &stk):重载=运算符
  • push(elem):向栈顶添加元素
  • pop():移除栈顶第一个元素
  • top():返回栈顶元素
  • empty():判断栈是否为空
  • size():返回栈的大小

3.5 queue容器

3.5.1 基本概念

queue是一种先进先出的数据结构,有两个出口

队列容器允许一端新增元素,一端移除元素

队列中只有队头和队尾才可以被外界使用,不允许遍历

3.5.2 queue接口

原型:

  • queue<T> que:默认构造
  • queue(const queue &que):拷贝构造
  • queue& operator=(const queue &que):重载=运算符
  • push(elem):往队尾添加元素
  • pop():从队头移除元素
  • back():返回最后一个元素
  • front():返回第一个元素
  • empty():判断队列是否为空
  • size():返回栈的大小

3.6 list容器

3.6.1 list基本概念

将数据进行链式存储

链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的

组成:由一系列结点组成

结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

STL中的链表是一个双向循环链表

链表的迭代器是双向迭代器,只支持前移和后移

优点:可以对任意位置进行快速插入和删除元素;采用动态存储分配,不会造成内存浪费和溢出

缺点:容器遍历速度慢于数组,占用空间比数组大

3.6.2 list构造函数

原型:

  • list<T> lst:默认构造
  • list(beg,end):拷贝构造,将beg和end之间的元素拷贝过来
  • list(n,elem):拷贝构造,将n个elem拷贝给对象
  • list(const list& lst):拷贝构造
3.6.3 list赋值和交换

原型:

  • assign(beg,end):将beg和end之间的元素拷贝过来
  • assign(n,elem):将n个elem拷贝赋值过来
  • list& operator=(const list &lst):重载=运算符
  • swap(lst):将lst与本身元素互换
3.6.4 list大小操作

原型:

  • size():返回容器内元素的个数
  • empty():判断容器是否为空
  • resize(n):重新指定容器长度为n,如果容器变长,就用默认值填充新位置;如果容器变短,就删去末尾超出长度的元素
  • resize(n,elem):如果容器变长,就用elem填充新位置
3.6.5 list插入和删除

原型:

  • push_back(elem):在容器尾部插入一个元素elem
  • pop_back():删除容器尾部第一个元素
  • push_fornt(elem):在容器开头插入一个元素elem
  • 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位置的元素,返回下一个元素的位置
  • remove(elem):删除容器中所有与elem值匹配的元素
3.6.7 list数据存取

原型:

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

list不支持随机访问

3.6.8 list反转和排序

原型:

  • reverse():反转链表
  • sort():链表排序。默认从小到大
  • sort(callback):排序算法的重载,回调函数callback指定排序规则

这里的sort()算法是链表的成员函数,不是algorithm库中的标准算法sort()。因为所有不支持随机访问迭代器的容器不能用标准算法,但是其内部会提供一些算法

3.7 set容器

3.7.1 set/multiset基本概念

set/multiset属于关联式容器,底层是二叉树实现

set插入数据只能用insert

set和multiset区别:

  • set不允许容器中有重复元素
  • multiset允许容器中有重复元素
3.7.2 set构造和赋值

原型:

  • set<T> st:默认构造
  • set(const set &st):拷贝构造
  • set& operator=(const set &st):重载=运算符

特点:所有元素插入时自动排序;不允许插入重复值

3.7.3 set大小和交换

原型:

  • size():返回容器的元素数量
  • empty():判断容器是否为空
  • swap(st):交换两个容器
3.7.4 set插入和删除

原型:

  • insert(elem):在容器中插入元素
  • clear():清除所有元素
  • erase(pos):删除pos迭代器指向的位置,返回下一个元素的位置
  • erase(beg,end):删除区间beg到end之间的所有元素,返回下一个元素的位置
  • erase(elem):删除容器中值为elem的元素

set在调用成员函数insert()时返回的数据类型为pair<iterator,bool>;而multiset调用insert()只返回迭代器,不返回布尔值,因为不用检测插入数据

3.7.5 set查找和统计

原型:

  • find(key):查找元素key是否存在,返回该元素的迭代器;若不存在则返回end()
  • count(key):统计元素key的数目,一般用于multiset;用于set返回值只能是0或1
3.7.6 set和multiset区别

区别;

  • set不会插入重复数据,而multiset可以
  • set插入数据时会返回插入结果,表示插入是否成功
  • multist插入时不会检测数据,所以可以插入重复数据
3.7.7 pair对组创建

成对出现的数据,利用对组pair可以返回两个数据

语法:

  • pair<type,type> p (value1,value2)
  • pair<type,type> p = make_pair(value1,value2)

make_pair()可以创建一个对组,并且不用写模板参数,只需要传入实参即可

使用:p.first访问对组的第一个值;p.second访问对组的第二个值

3.7.8 set容器排序

利用仿函数指定排序规则

仿函数就是定义一个类,类中实现()的重载,使类的使用和函数的使用方法一样

#include<set>

class MyCompare
{
public:
    bool operator()(int v1, int v2) const//需要加一个const修饰符表示常函数
    {
        return v1 > v2;
    }
};

set<int,MyCompare> st;//用仿函数的规则进行排序

3.8 map/multimap容器

3.8.1 map基本概念

map的每一个元素都是pair类型的,pair中的第一个值称为key(键值),用于索引,第二个值称为value,是实值

所有元素都会根据键值自动排序

map/multimap是关联式容器,底层结构用二叉树实现

map和multimap区别:

  • map不允许重复key值
  • multimap允许重复key值
3.8.2 map构造和赋值

原型:

  • map<T1, T2> mp:默认构造
  • map<const map &map> mp:拷贝构造
  • map& operator=(const map &mp):重载=运算符,赋值

map中元素都是成对出现的,插入时需要使用对组pair进行插入

3.8.3 map大小和交换

原型:

  • size():返回容器中元素的数组
  • empty():判断容器是否为空
  • swap(st):交换两个容器
3.8.4 map插入和删除

原型:

  • insert(elem):在容器中插入元素
  • clear():清空所有元素
  • erase(pos):删除pos迭代器指向的元素,返回下一个元素的迭代器
  • erase(beg,end):删除从beg到end之间的元素,返回下一个元素的迭代器
  • erase(key):删除容器中键为key的元素

插入insert的重载:①insert(pair<int,int>(1,1)),利用对组pair进行插入;②insert(make_pair(1,1)),利用make_pair创建对组进行插入;③insert(map<int,int>::value_type(1,1)),利用map下的一种数据类型value_type进行插入;④m[1] = 1,利用重载运算符[]来插入。缺点如果没有初始化,访问其value时会返回一个默认值。一般用于访问确定存在的value

3.8.5 map查找和统计

原型:

  • find(key):查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end()
  • count(key):统计key的元素个数。使用map返回值只可能是0或1,只有使用multimap返回值才可能大于1
3.8.6 map容器排序

map中排序规则默认为按照key值从小到大排序

利用仿函数改变排序规则

#include<map>

class MyCompare
{
public:
    bool operator()(int v1, int v2)
    {
        return v1 > v2;//降序
    }
};

map<int, int, MyCompare> m;

4. STL函数对象

4.1 函数对象

4.1.1 函数对象概念

定义:重载函数调用操作符的类,其对象常称为函数对象

函数对象使用重载的()时,行为类似函数调用,也叫仿函数

本质:函数对象(仿函数)是一个类,不是一个函数

4.1.2 函数对象使用

特点:

  • 函数对象在使用时,可以项普通函数一样调用,可以有参数,可以有返回值
  • 函数对象超出普通函数的概念,函数对象可以有自己的状态
  • 函数对象可以作为参数传递
class MyAdd
{
public:
    MyAdd()
    {
        count = 0;//在构造函数时对count进行初始化
    }
    int operator()(int a, int b)
    {
        count++;//因为是成员属性,所以对于同一个对象是可以累加的
        return a+b;
    }
    int count;//内部成员属性
}

//函数对象作参数
void test(MyAdd & ma, int a, int b)
{
    ma(a,b);//使用方法不变
}

int main()
{
    MyAdd myadd;
    int c = myadd(10,10);//c=20
    test(myAdd,10,10);
}

4.2 谓词

4.2.1 谓词概念

概念:

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

STL算法模板中,参数名为Pred的一般就是谓词

4.2.2 一元谓词

使用:find_if(v.begin(),v.end(),MyAdd())其中MyAdd()匿名函数对象,和匿名对象的用法一样,是一元谓词作为参数传入算法中

4.2.3 二元谓词

使用:sort(v.begin(),v.end(),MyCompare())其中MyCompare()仍然是匿名函数对象,是二元谓词

4.3 内建函数对象

4.3.1 内建函数对象意义

STL提供的一些仿函数,需要引入头文件#include<functional>

分类:算术仿函数关系仿函数逻辑仿函数

4.3.2 算术仿函数

实现四则运算

其中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>:取反仿函数
4.3.3 关系仿函数

实现关系对比

原型:

  • 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):小于等于
4.3.4 逻辑仿函数

实现逻辑运算

原型:

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

transform算法:transform(v1.begin(), v1,end(), v2.begin(), Func())作用是将v1容器中begin到end中的所有元素搬运到v2中begin之后,并在搬运过程中利用Func()对每个元素进行操作。注意搬运时v2容器的空间必须已经开辟,否则将搬运失败

5. STL常用算法

算法主要由头文件#include<algorithm>#include<functional>#include<numeric>组成

algorithm:是所有STL中最大的一个,包含比较、交换、查找、遍历、复制、修改

functional:体积很小,只包括几个序列上进行简单数学运算的模板

numeric:定义了一些模板类用以声明函数对象

5.1 常用遍历算法

需要导入头文件#include<algorithm>

5.1.1 for_each

遍历容器

原型:for_each(beg,end,func):参数beg指起始迭代器,end指结束迭代器;func指函数或者函数对象,其中如果为函数,调用时只写函数名,如果为函数对象,需要写函数名加括号。因为函数名表示函数指针,实际传入的参数是指向该函数的指针,所以不加括号,函数对象加上括号才表示一个对象,所以需要加括号

5.1.2 transform

搬运容器

原型:transform(beg1,end1,beg2,func)参数beg1指第一个容器的起始迭代器;end1指第一个容器的结束迭代器;beg2指第二个容器的起始迭代器;func指函数或者函数对象

标容器需要提前开辟足量的空间,否则搬运失败target.resize(int)

5.2 常用查找算法

需要导入头文件#include<algorithm>

5.2.1 find

查找指定元素,返回指定元素的迭代器,找不到返回结束迭代器end

原型:find(beg,end,value)参数beg指起始迭代器;end指结束迭代器;value指需要查找的元素

当寻找的元素为自定义数据类型时,需要重载==符号

5.2.2 find_if

按条件查找元素,返回指定元素的迭代器,找不到返回结束迭代器end

原型:find_if(beg,end,Pred)参数beg指起始迭代器;end指结束迭代器;Pred指函数或谓词

5.2.3 adjacent_find

查找相邻重复元素,返回相邻元素的第一个位置的迭代器,找不到返回结束迭代器end

原型:adjacent_find(beg,end)参数beg指起始迭代器;end指结束迭代器

5.2.4 binary_search

查找指定元素是否存在,找到返回true,找不到返回false

二分查找,只能在有序序列中查找

原型:bool binary_search(beg,end,value)参数beg指起始迭代器;end指结束迭代器;value指查找的元素

5.2.5 count

统计元素个数,返回元素个数

原型:count(beg,end,value)参数beg指起始迭代器;end指结束迭代器;value指需要统计的元素

5.2.6 count_if

按条件统计元素个数,返回元素个数

原型:count(beg,end,Pred)参数beg指起始迭代器;end指结束迭代器;Pred指函数对象

5.3 常用排序算法

需要导入头文件#include<algorithm>

5.3.1 sort

对容器内元素进行排序

原型:sort(beg,end,Pred)参数beg指起始迭代器;end指结束迭代器;Pred指函数对象,不填默认从小到大排序

5.3.2 random_shuffle

洗牌,指定范围内的元素随机调整次序,打乱序列

原型:random_shuffle(beg,end)参数beg指起始迭代器;end指结束迭代器

如果不加随机数种子,每次执行洗牌算法打乱的序列是一样的,所以可以利用系统时间构造随机数种子实现洗牌srand((unsigned int)time(NULL)),调用time函数需要引入头文件#include<ctime>

5.3.3 merge

两个容器合并,并存储到另一个容器中

两个容器必须是有序的,合并后仍为有序序列

原型:merge(beg1,end1,beg2,end2,dest)参数beg1指第一个容器的起始迭代器;end1指第一个容器的结束迭代器;beg2指第二个容器的起始迭代器;end2指第二个容器的结束迭代器;dest指目标容器起始迭代器

目标容器需要提前分配足够大的空间target.resize(int)

5.3.4 reverse

将容器元素顺序进行反转

原型:reverse(beg,end)参数beg指起始迭代器;end指结束迭代器

5.4 常用拷贝和替换算法

需要导入头文件#include<algorithm>

5.4.1 copy

将容器内指定范围内的元素拷贝到另一个容器中

原型:copy(beg,end,dest)参数beg是已知容器的起始迭代器;end是已知容器的结束迭代器;dest是目标容器的起始迭代器

需要为目标容器提前开辟足够大的空间,否则拷贝失败

5.4.2 replace

将容器内的指定范围的旧元素修改为新元素

原型:replace(beg,end,oldvalue,newvalue)参数beg指起始迭代器;end指结束迭代器;oldvalue值旧元素的值;newvalue指新元素的值

会替换范围中的所有等于oldvalue的值

5.4.3 replace_if

将区间内满足条件的元素,替换成指定元素

原型:replace_if(beg,end,Pred,newvalue)参数beg指起始迭代器;end指结束迭代器;Pred指谓词即函数对象;newvalue指新元素的值

5.4.4 swap

互换两个容器中的所有元素,只能进行同种容器之间的交换

原型:swap(c1,c2)参数c1为第一个容器;c2为第二个容器

容器大小也会交换

5.5 常用算术生成算法

需要导入头文件#include<numeric>

5.5.1 accumulate

计算区间内容器元素累积总和,并返回

原型:accumulate(beg,end,value)参数beg指起始迭代器;end指结束迭代器;value指起始值

5.5.2 fill

向容器中填充的元素

原型:fill(beg,end,value)参数beg指起始迭代器;end指结束迭代器;value指填充的值

需要提前为容器开辟空间

5.6 常用集合算法

5.6.1 set_intersection

求两个容器的交集,并返回交集中最后一个元素的迭代器

两个容器必须是有序序列

原型:set_intersection(beg1,end1,beg2,end2,dest)参数beg1指第一个容器的起始迭代器;end1指第一个容器的结束迭代器;beg2指第二个容器的起始迭代器;end2指第二个容器的结束迭代器;dest指目标容器的起始迭代器

目标容器需要提前开辟空间,大小为两个容器中较小的容器大小

遍历交集时,结束迭代器使用算法返回的迭代器而不用end,这样能够准确遍历交集

5.6.2 set_union

求两个集合的并集,返回并集的最后一个元素的迭代器

两个容器都必须是有序序列

原型:set_union(beg1,end1,beg2,end2,dest)参数beg1指第一个容器的起始迭代器;end1指第一个容器的结束迭代器;beg2指第二个容器的起始迭代器;end2指第二个容器的结束迭代器;dest指目标容器的起始迭代器

目标容器需要提前开辟空间,大小为两个容器中较大的容器大小

遍历并集时,结束迭代器使用算法返回的迭代器而不用end,这样能够准确遍历并集

5.6.3 set_difference

求两个集合的差集,返回差集的最后一个元素的迭代器

两个容器都必须是有序序列

原型:set_difference(beg1,end1,beg2,end2,dest)参数beg1指第一个容器的起始迭代器;end1指第一个容器的结束迭代器;beg2指第二个容器的起始迭代器;end2指第二个容器的结束迭代器;dest指目标容器的起始迭代器

目标容器需要提前开辟空间,大小为第一个容器的大小

遍历差集时,结束迭代器使用算法返回的迭代器而不用end,这样能够准确遍历差集

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本资源是压缩包形式的, 里面包含 本书,里面是pdf格式的, 带书签目录,本书是完整版的。 资源都是我自己用过的,不骗大家。 本书作者: 肖俊宇 吴为胜; 出版社: 电子工业出版社 内容简介: 《由浅入深学C++基础进阶与必做300题(含DVD光盘1张)》是C++语言的入门教程,较为系统地介绍了C++语言的基础内容。本书共分为3篇22章,详细介绍了C++语言的基础知识、面向对象、标准模块、底层开发和综合案例。本书循序渐进地讲述了C++基础知识、C++程序的组成及其开发过程、C++程序中的数据、表达式和语句、控制程序流程、数组与字符串、指针与引用、使用函数、函数模板、错误和异常处理、宏和预编译、面向对象的开发、封装、继承、多态、类模板、文件流、标准模板库STL和编程实践等内容。 《由浅入深学C++基础进阶与必做300题(含DVD光盘1张)》涉及面广,从基本知识到高级内容和核心概念,再到综合案例,几乎涉及C++开发的所有重要知识。本书适合所有想全面学习C++开发技术的人员阅读,尤其适合没有编程基础C++语言初学者作为入门教程,也可作为大、中院校师生和培训班的教材,对于C++语言开发爱好者,本书也有较大的参考价值。 章节目录: 第1篇 C++基础篇 第1章 C++概述 1 1.1 引言 1 1.1.1 C++的历史沿革 1 1.1.2 入门C++ 2 1.1.3 编程思想的转变 3 1.2 C++概述 4 1.2.1 C++的特征 5 1.2.2 C与C++的比较 5 1.2.3 C++的应用领域 6 1.3 C++源程序的组成 6 1.3.1 基本组成元素 7 1.3.2 标识符 8 1.3.3 保留字 8 1.3.4 符号 8 1.4 C++集成开发环境——DEV-C++ 9 1.4.1 选择C++编译器 9 1.4.2 安装DEV-C++ 10 1.4.3 DEV-C++ IDE简介 11 1.5 第一个C++程序——Hello World 11 1.5.1 创建源程序 11 1.5.2 编译运行 13 1.6 小结 14 1.7 习题 14 第2章 变量与数据类型 18 2.1 常量和变量 18 2.1.1 常量 18 2.1.2 变量 21 2.1.3 变量的定义及赋值 22 2.1.4 变量的应用示例 24 2.2 基本数据类型 25 2.2.1 基本数据类型概述 25 2.2.2 整型数据类型 26 2.2.3 浮点型数据类型 27 2.2.4 字符型数据类型 29 2.2.5 布尔型数据类型 30 2.3 变量的作用域 31 2.4 类型转换 32 2.4.1 隐式转换 32 2.4.2 显式转换 33 2.5 小结 34 2.6 习题 34 第3章 表达式与语句 39 3.1 运算符 39 3.1.1 运算符概述 39 3.1.2 算术运算符 40 3.1.3 自增和自减运算符 42 3.1.4 赋值运算符 43 3.1.5 关系运算符 44 3.1.6 逻辑运算符 45 3.1.7 条件运算符 46 3.1.8 逗号运算符 47 3.1.9 位运算符 48 3.1.10 sizeof运算符 49 3.2 运算符的优先级和结合性 50 3.3 表达式 51 3.4 语句 53 3.4.1 空格的作用 53 3.4.2 语句块 54 3.4.3 赋值语句 55 3.4.4 空语句 56 3.5 小结 57 3.6 习题 57 第4章 流程控制结构之顺序结构 63 4.1 程序流程图 63 4.2 表达式语句 64 4.3 格式化输入/输出 65 4.3.1 标准输入流cin 65 4.3.2 标准输出流cout 66 4.3.3 输出流cerr和clog 68 4.4 格式控制函数 69 4.5 格式控制符 71 4.5.1 控制不同进制的输出 72 4.5.2 控制输出宽度 72 4.5.3 控制输出精度 73 4.6 顺序结构综合应用 74 4.7 小结 75 4.8 习题 75

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值