c++基础
1. c++简介
C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。
C++ 被认为是一种中级语言,它综合了高级语言和低级语言的特点。
注意:使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。
C++ 完全支持面向对象的程序设计,包括面向对象开发的四大特性:
- 封装:封装是将数据和方法组合在一起,对外部隐藏实现细节,只公开对外提供的接口。这样可以提高安全性、可靠性和灵活性。
- 继承:继承是从已有类中派生出新类,新类具有已有类的属性和方法,并且可以扩展或修改这些属性和方法。这样可以提高代码的复用性和可扩展性。
- 多态:多态是指同一种操作作用于不同的对象,可以有不同的解释和实现。它可以通过接口或继承实现,可以提高代码的灵活性和可读性。
- 抽象:抽象是从具体的实例中提取共同的特征,形成抽象类或接口,以便于代码的复用和扩展。抽象类和接口可以让程序员专注于高层次的设计和业务逻辑,而不必关注底层的实现细节。
2. 基础知识
1. 变量和数据类型
使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。
c++中七种基本的 C++ 数据类型及其扩展:
类型 | 位 | 范围 |
---|---|---|
char | 1 | -128到127 或者 0到255 |
unsigned char | 1 | 0到255 |
signed char | 1 | -128到127 |
int | 4 | -2147483648 到 2147483647 |
unsigned int | 4 | 0 到 4294967295 |
signed int | 4 | -2147483648 到 2147483647 |
short int | 2 | -32768 到 32767 |
unsigned short int | 2 | 0 到 65,535 |
signed short int | 2 | -32768 到 32767 |
long int | 8 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
signed long int | 8 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
unsigned long | 8 | 0 到 18,446,744,073,709,551,615 |
float | 4 | 单精度型浮点型 |
double | 8 | 双精度型浮点型 |
long long | 8 | |
long double | 16 | |
wchar_t | 2或4 | 1个宽字符 |
类型转换
类型转换是将一个数据类型的值转换为另一种数据类型的值。C++ 中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换。
静态转换
静态转换是将一种数据类型的值强制转换为另一种数据类型的值。
静态转换通常用于比较类型相似的对象之间的转换,例如将 int 类型转换为 float 类型。
静态转换不进行任何运行时类型检查,因此可能会导致运行时错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19void func(void* ptr) { //其他类型指针->void*指针->其他类型指针
double* pp = static_cast<double*>(ptr); //将指针类型转换的函数
}
int main() {
int a = 3;
long b = a; //安全,可以隐式转换,不会出现警告
double c = 1.23;
long d = (long)c; //C风格:显式转换
long d1 = static_cast<long>(c); //c++风格:显式转换
//double* pd1 = &a; //错误,不能隐式转换
double* pd2 = (double*)&a; //c风格,强制类型转换
//double* pd3 = static_cast<double*>(&a); //错误,static_cast不支持不同类型指针的转换,但可以通过void*指针中转
void* pv = &a; //任何类型的指针都可以隐式的转换为void*(无符号型指针)
double* pd3 = static_cast<double*>(pv); //然后通过static_cast将void*转换为其他类型的指针
cout << pd3 << endl;
}
动态转换
动态转换通常用于将一个基类指针或引用转换为派生类指针或引用。动态转换在运行时进行类型检查,如果不能进行转换则返回空指针或引发异常。
1
2
3
4class Base {};
class Derived : public Base {};
Base* ptr_base = new Derived;
Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); // 将基类指针转换为派生类指针
常量转换
常量转换用于将 const 类型的对象转换为非 const 类型的对象。
常量转换只能用于转换掉 const 属性,不能改变对象的类型。
1
2const int i = 10;
int& r = const_cast<int&>(i); // 常量转换,将const int转换为int
重新解释转换
重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。
重新解释转换不进行任何类型检查,因此可能会导致未定义的行为。
int i = 10; float f = reinterpret_cast<float&>(i); // 重新解释将int类型转换为float类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
```c++
#include<iostream>
using namespace std; //使用std这个命名空间
int number; //全局变量可以不用赋值,默认为0
int main() {
int number = 1; //局部变量
cout << "number=" << number << endl;
cout << "::number=" << ::number << endl; //访问全局变量前要加::
//定义常量
const float Pi = 3.14;
cout << "Pi=" << Pi << endl;
//特殊字符
cout << "hello world\t\"hello world\"\n \?" << endl;
}
c++中有两种类型的表达式:
- 左值:指向内存位置的表达式被称为左值表达式。左值可以出现在赋值号的左边或右边。
- 右值:术语右值指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
c++中的变量作用域:
- 局部作用域:在函数内部声明的变量具有局部作用域,它们只能在函数内部访问。局部变量在函数每次被调用时被创建,在函数执行完后被销毁。
- 全局作用域:在所有函数和代码块之外声明的变量具有全局作用域,它们可以被程序中的任何函数访问。全局变量在程序开始时被创建,在程序结束时被销毁。
- 块作用域:在代码块内部声明的变量具有块作用域,它们只能在代码块内部访问。块作用域变量在代码块每次被执行时被创建,在代码块执行完后被销毁。
- 类作用域:在类内部声明的变量具有类作用域,它们可以被类的所有成员函数访问。类作用域变量的生命周期与类的生命周期相同。
2. 修饰符类型
类型限定符提供了变量的额外信息,用于在定义变量或函数时改变它们的默认行为的关键字。
限定符 | 含义 |
---|---|
const | const定义常量,表示该变量的值不能被修改 |
volatile | 修饰符volatile告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程 |
restrict | 由restrict修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict |
mutable | 表示类中的成员变量可以在 const 成员函数中被修改 |
static | 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问 |
register | 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率 |
1 | const int NUM = 10; // 定义常量 NUM,其值不可修改 |
3. 运算符
1 | int main() { |
输出的结果:
1 | i=0 |
4.流程控制
1.范围for循环
1 | int main() { |
2.99乘法表
1 | int main(){ |
3.绘制爱心曲线
1 | int main() { |
5. 指针与常量
1 | int main() { |
6. 引用与常量
1 | int main() { |
常量引用:通过使用 const int&
,你可以创建一个引用,该引用可以引用常量(const int)、变量(int)或字面值(10),但你不能通过这个引用修改其引用的值。
引用与指针常量的区别
引用:
- 绑定后不可变,始终指向同一个变量
- 可以通过引用来修改其所绑定变量的值
指针常量:
- 绑定后不可变,指向的对象不能改变
- 可以通过指针来修改其所指向变量的值
结论:引用和指针常量在绑定后都是不能再绑定到其他变量,但它们的用法和语义上有明显的区别。引用更像是一个别名,而指针常量则是一个变量,其值是一个内存地址。
7. 对象特性
1.构造函数和析构函数
构造函数和析构函数都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构
构造函数
- 没有返回值,不用写void
- 函数名与类名相同
- 构造函数可以有参数,可以发生重载
- 创建对象的时候,构造函数会自动调用,而且只调用一次
析构函数(进行清理的操作)
- 没有返回值,不写void
- 函数名和类名相同,在名称前加~
- 析构函数不可以有参数的,不可以发生重载
- 对象在销毁前,会自动调用析构函数,而且只调用一次
2.成员变量和成员函数分开存储
空对象占用内存空间为1,c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置。
下面这个程序中,当Person类中有int num
的时候,程序输出为4,当没有int num
的时候,输出的结果为1。
1 | class Person { |
3.this指针和指针常量
this指针的本质是指针常量,指针的指向是不可以修改的
1 | class Person { |
8. 运算符重载
1.赋值运算符重载
1 | //在普通赋值运算符情况下,当有些属性创建在堆区,此时就会出现浅拷贝的问题 |
2.关系运算符
1 | class Person { |
3.加号运算符重载
1 | //运算符重载:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型 |
4.左移运算符重载
1 | class Person { |
9. 继承
1.以public方式继承父类
- 父类中的公共权限成员 到 子类中依然是公共权限
- 父类中的保护权限成员 到 子类中依然是保护权限
- 父类中的私有权限成员 在 子类中访问不到
2.以protected方式继承父类
- 父类中的公共权限成员 到 子类中是保护权限
- 父类中的保护权限成员 到 子类中是保护权限
- 父类中的私有权限成员 在 子类访问不到
3.以private方式继承父类
- 父类中的公共权限成员 到 子类中是私有权限
- 父类中的保护权限成员 到 子类中是私有权限
- 父类中的私有权限成员 在 子类访问不到
注意:继承中的构造函数和析构函数的顺序:先构造父类,再构造子类,析构函数顺序与构造函数顺序相反
如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数。如果想访问到父类中被隐藏的同名成员函数,需要加作用域
菱形继承
1 | class Animal { |
10. 多态
1.多态满足的条件
- 有继承关系
- 子类重写父类的虚函数(重写:函数返回值类型、函数名、参数列表 完成相同)
多态使用:父类的指针或者引用 指向子类对象
2.纯虚函数:只要有一个纯虚函数,这个类就是抽象类
抽象类的特定:
- 无法实例化
- 抽象类的子类,必须要重写父类中的纯虚函数,否则也属于抽象类
1 | class Base { |
3.虚析构函数和纯虚析构函数共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
区别:如果是纯虚析构函数,该类是抽象类,无法实例化对象。
虚析构函数
1 | class Base { |
纯虚析构函数
1 | class Base { |
11. 模板
1.函数模板的应用:建立一个通用函数,其函数反回值类型和形参可以不具体制定,用一个虚拟的类型来表示
1 | template<typename T> //声明一个模板,告诉编译器后面代码种紧跟这的T不要报错,T是一个通用数据类型 |
2.调用规则
- 如果函数模板和普通函数都可以调用,优先调用普通函数
- 可以通过空模板参数列表 强制调用 函数模板
- 函数模板可以发生函数重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
1 | void myprint(int a, int b) { |
3.函数模板的全特化
模板的调用不是万能的,当对自定义类型数据等进行比较时,会有问题。利用全特化的模板,可以解决自定义类型的通用性
1 | template<typename T> |
模板全特化 :
template<>
表示这是一个模板的完全特化版本。也就是说,这个mycompare
函数专门用于person
类型的对象比较。- 全特化意味着
mycompare
函数模板的原型存在,但这里的版本仅适用于person
类型。
4.类模板
类模板与函数模板的区别
- 类模板没有自动类型推导的使用方法
- 类模板在模板参数列表中可以有默认参数
1 | template<class NameType,class AgeType> |
类模板中的成员函数并不是一开始就创建的,在调用时才去创建的。
2. STL教程
C++ 标准模板库是一套功能强大的 C++ 模板类和函数的集合,它提供了一系列通用的、可复用的算法和数据结构。
STL 的设计基于泛型编程,这意味着使用模板可以编写出独立于任何特定数据类型的代码。
STL 分为多个组件,包括容器、迭代器、算法、函数对象和适配器等。
1. array容器
std::array
是 C++ 标准库中的一个模板类,它定义在 <array>
头文件中。std::array
模板类提供了一个固定大小的数组,其大小在编译时确定,并且不允许动态改变。与 C 语言中的数组相比,具有更好的类型安全和内存管理特性。
1.std::array的基本语法:
std::array<T, N> array_name;
T
是数组中元素的类型。N
是数组的大小,必须是一个非负整数。
声明与初始化
1 | int main() { |
2.特点
- 类型安全:
std::array
强制类型检查,避免了 C 语言数组的类型不安全问题。 - 固定大小:数组的大小在编译时确定,不能在运行时改变。
- 内存连续:
std::array
的元素在内存中是连续存储的,这使得它可以高效地访问元素。 - 标准容器:
std::array
提供了与std::vector
类似的接口,如size()
,at()
,front()
,back()
等。
3.常用的一些函数
函数 | 说明 |
---|---|
at(size_t pos) |
返回指定位置的元素,带边界检查 |
operator[] |
返回指定位置的元素,不带边界检查 |
front() |
返回数组的第一个元素 |
back() |
返回数组的最后一个元素 |
data() |
返回指向数组数据的指针 |
size() |
返回数组大小(固定不变) |
fill(const T& value) |
将数组所有元素设置为指定值 |
swap(array& other) |
交换两个数组的内容 |
begin() / end() |
返回数组的起始/结束迭代器 |
4.特性
特性 | std::array |
---|---|
大小 | 编译时固定 |
边界检查 | at() 提供边界检查 |
内存管理 | 栈上分配 |
性能 | 高效 |
接口 | 支持 STL 标准接口 |
2. vector容器
vector
是 STL 中的一个容器类,用于存储动态大小的数组。vector
是一个序列容器,它允许用户在容器的末尾快速地添加或删除元素。与数组相比,<vector>
提供了更多的功能,如自动调整大小、随机访问等。
1.声明与初始化
1 | int main() { |
2.常用的一些函数
函数 | 说明 |
---|---|
push_back(const T& val) |
在末尾添加元素 |
pop_back() |
删除末尾元素 |
at(size_t pos) |
返回指定位置的元素,带边界检查 |
operator[] |
返回指定位置的元素,不带边界检查 |
front() |
返回第一个元素 |
back() |
返回最后一个元素 |
data() |
返回指向底层数组的指针 |
size() |
返回当前元素数量 |
capacity() |
返回当前分配的容量 |
reserve(size_t n) |
预留至少 n 个元素的存储空间 |
resize(size_t n) |
将元素数量调整为 n |
clear() |
清空所有元素 |
insert(iterator pos, val) |
在指定位置插入元素 |
erase(iterator pos) |
删除指定位置的元素 |
begin() / end() |
返回起始/结束迭代器 |
3.特性
特性 | std::vector |
---|---|
大小 | 动态可变 |
存储位置 | 连续内存 |
访问性能 | 随机访问快速 |
插入和删除性能 | 末尾操作性能高,其他位置较慢 |
内存增长方式 | 容量不足时成倍增长 |
3. list容器
<list>
是 C++ 标准模板库中的一个序列容器,它允许在容器的任意位置快速插入和删除元素。与数组或向量<vector>
不同,list
不需要在创建时指定大小,并且可以在任何位置添加或删除元素,而不需要重新分配内存。
1.声明和初始化
1 | int main() { |
2.特点
- 双向迭代:
<list>
提供了双向迭代器,可以向前和向后遍历元素。 - 动态大小:与数组不同,
<list>
的大小可以动态变化,不需要预先分配固定大小的内存。 - 快速插入和删除:可以在列表的任何位置快速插入或删除元素,而不需要像向量那样移动大量元素。
3.常用的一些函数
函数 | 说明 |
---|---|
push_back(const T& val) |
在链表末尾添加元素 |
push_front(const T& val) |
在链表头部添加元素 |
pop_back() |
删除链表末尾的元素 |
pop_front() |
删除链表头部的元素 |
insert(iterator pos, val) |
在指定位置插入元素 |
erase(iterator pos) |
删除指定位置的元素 |
clear() |
清空所有元素 |
size() |
返回链表中的元素数量 |
empty() |
检查链表是否为空 |
front() |
返回链表第一个元素 |
back() |
返回链表最后一个元素 |
remove(const T& val) |
删除所有等于指定值的元素 |
sort() |
对链表中的元素进行排序 |
merge(list& other) |
合并另一个已排序的链表 |
reverse() |
反转链表 |
begin() / end() |
返回链表的起始/结束迭代器 |
4.特性
特性 | std::list |
---|---|
内存结构 | 非连续内存,双向链表 |
访问性能 | 顺序访问较快,随机访问慢 |
插入/删除性能 | 任意位置插入、删除快 |
适用场景 | 频繁在中间插入/删除 |
迭代器稳定性 | 稳定,元素插入或删除不会失效 |
5.注意事项
<list>
的元素是按插入顺序存储的,而不是按元素值排序。- 由于
<list>
的元素存储在不同的内存位置,所以它不适合需要随机访问的场景。 - 与向量相比,
<list>
的内存使用效率较低,因为每个元素都需要额外的空间来存储指向前后元素的指针。
4. deque容器
<deque>
提供了双端队列的实现,它在C++中以模板类的形式存在,允许存储任意类型的数据。
<deque>
是一个动态数组,它提供了快速的随机访问能力,同时允许在两端进行高效的插入和删除操作。这使得 <deque>
成为处理需要频繁插入和删除元素的场景的理想选择。
1.声明和初始化
1 | int main() { |
2.常用的一些函数
函数名称 | 功能描述 |
---|---|
operator= |
赋值操作符,赋值给 deque 容器。 |
assign() |
用新值替换 deque 容器中的所有元素。 |
at(size_type pos) |
返回 pos 位置的元素,并进行范围检查。 |
operator[](size_type pos) |
返回 pos 位置的元素,不进行范围检查。 |
front() |
返回第一个元素的引用。 |
back() |
返回最后一个元素的引用。 |
begin() |
返回指向第一个元素的迭代器。 |
end() |
返回指向末尾元素后一位置的迭代器。 |
rbegin() |
返回指向最后一个元素的逆向迭代器。 |
rend() |
返回指向第一个元素之前位置的逆向迭代器。 |
empty() |
检查容器是否为空。 |
size() |
返回容器中的元素个数。 |
max_size() |
返回容器可容纳的最大元素个数。 |
clear() |
清除容器中的所有元素。 |
insert(iterator pos, const T& value) |
在 pos 位置插入 value 元素。 |
erase(iterator pos) |
移除 pos 位置的元素。 |
push_back(const T& value) |
在容器末尾添加 value 元素。 |
pop_back() |
移除容器末尾的元素。 |
push_front(const T& value) |
在容器前端添加 value 元素。 |
pop_front() |
移除容器前端的元素。 |
resize(size_type count) |
调整容器大小为 count ,多出部分用默认值填充。 |
swap(deque& other) |
交换两个 deque 容器的内容。 |
get_allocator() |
返回一个用于构造双端队列的分配器对象的副本。 |
注意:在使用 front() 或 back() 之前,确保双端队列不为空,否则会引发未定义的行为。如果需要检查双端队列是否为空,可以使用 empty() 成员函数。
5. stack容器
<stack>
是 C++ 标准模板库的一部分,它实现了一个后进先出的数据结构。这种数据结构非常适合于需要”最后添加的元素最先被移除”的场景。
<stack>
容器适配器提供了一个栈的接口,它基于其他容器(如 deque
或 vector
)来实现。栈的元素是线性排列的,但只允许在一端(栈顶)进行添加和移除操作。
1.常用的操作
push()
: 在栈顶添加一个元素。pop()
: 移除栈顶元素。top()
: 返回栈顶元素的引用,但不移除它。empty()
: 检查栈是否为空。size()
: 返回栈中元素的数量。
2.注意事项
<stack>
不提供直接访问栈中元素的方法,只能通过top()
访问栈顶元素。- 尝试在空栈上调用
top()
或pop()
将导致未定义行为。 <stack>
的底层容器可以是任何支持随机访问迭代器的序列容器,如vector
或deque
。
6. queue容器
C++ 标准库中的 <queue>
头文件提供了队列数据结构的实现。队列是一种先进先出的数据结构,它允许在一端添加元素(称为队尾),并在另一端移除元素(称为队首)。
1.常用的操作
empty()
: 检查队列是否为空。size()
: 返回队列中的元素数量。front()
: 返回队首元素的引用。back()
: 返回队尾元素的引用。push()
: 在队尾添加一个元素。pop()
: 移除队首元素。
2.注意事项
- 队列不允许随机访问元素,即不能直接通过索引访问队列中的元素。
- 队列的实现通常使用链表或动态数组,这取决于具体的实现。
7. priority_queue容器
在 C++ 中,<priority_queue>
是标准模板库的一部分,用于实现优先队列。优先队列是一种特殊的队列,它允许我们快速访问队列中具有最高(或最低)优先级的元素。
在 C++ 中,priority_queue
默认是一个大顶堆,这意味着队列的顶部元素总是具有最大的值。
priority_queue
是一个容器适配器,它提供了对底层容器的堆操作。它不提供迭代器,也不支持随机访问。
1.常用的操作
empty()
: 检查队列是否为空。size()
: 返回队列中的元素数量。top()
: 返回队列顶部的元素(不删除它)。push()
: 向队列添加一个元素。pop()
: 移除队列顶部的元素。
1 | //自定义类型,使用优先队列 |
8. set容器
C++ 标准库中的 <set>
是一个关联容器,它存储了一组唯一的元素,并按照一定的顺序进行排序。
<set>
提供了高效的元素查找、插入和删除操作。它是基于红黑树实现的,因此具有对数时间复杂度的查找、插入和删除性能。
<set>
容器中存储的元素类型必须满足以下条件:
- 元素类型必须可以比较大小。
- 元素类型必须可以被复制和赋值。
1.常用的操作
insert(元素)
: 插入一个元素。erase(元素)
: 删除一个元素。find(元素)
: 查找一个元素。size()
: 返回容器中元素的数量。empty()
: 检查容器是否为空。
2.set容器的特点:
- 所有元素插入时会自动排序
- set容器不存在重复的值
3.unordered_set容器
提供了一种基于哈希表的容器,用于存储唯一的元素集合。与 set
不同,unordered_set
不保证元素的自动排序,但通常提供更快的查找、插入和删除操作。常用的操作和set容器大致一样。
9.map容器
<map>
是标准模板库的一部分,它提供了一种关联容器,用于存储键值对。
map
容器中的元素是按照键的顺序自动排序的,这使得它非常适合需要快速查找和有序数据的场景。
1.定义和特性
- 键值对:
map
存储的是键值对,其中每个键都是唯一的。 - 排序:
map
中的元素按照键的顺序自动排序,通常是升序。 - 唯一性:每个键在
map
中只能出现一次。 - 双向迭代器:
map
提供了双向迭代器,可以向前和向后遍历元素。
2.常用的操作
1 | int main() { |
通过key设置排序方式
1 | class mycompare { |
3. algorithm算法库
C++ 标准库中的 <algorithm>
头文件提供了一组用于操作容器(如数组、向量、列表等)的算法。这些算法包括排序、搜索、复制、比较等,它们是编写高效、可重用代码的重要工具。
<algorithm>
头文件定义了一组模板函数,这些函数可以应用于任何类型的容器,只要容器支持迭代器。这些算法通常接受两个或更多的迭代器作为参数,表示操作的起始和结束位置。
1. 排序算法sort
定义:对容器中的元素进行排序。
语法:sort(container.begin(), container.end(), compare_function);
其中 compare_function 是一个可选的比较函数,用于自定义排序方式。
其它算法:
- std::partial_sort: 对部分区间排序
- std::stable_sort: 稳定排序,保留相等元素的相对顺序。
1 | int main() { |
2. 搜索算法find
定义:在容器中查找与给定值匹配的第一个元素。
语法:auto it = find(container.begin(), container.end(), value);
如果找到,it 将指向匹配的元素;如果没有找到,it 将等于 container.end()。
其它算法:
- std::binary_search: 对有序区间进行二分查找。
- std::find_if: 查找第一个满足特定条件的元素。
1 | int main() { |
3. 复制算法copy
定义:将一个范围内的元素复制到另一个容器或数组。
语法:copy(source_begin, source_end, destination_begin);
1 | int main() { |
4. 比较算法equal
定义:比较两个容器或两个范围内的元素是否相等。
语法:bool result = equal(first1, last1, first2, compare_function);
1 | int main() { |
5. 修改算法
常用的语法:
- std::reverse: 反转区间内的元素顺序。
std::reverse(vec.begin(), vec.end());
- std::fill: 将指定区间内的所有元素赋值为某个值。
std::fill(vec.begin(), vec.end(), 0); // 所有元素设为 0
- std::replace: 将区间内的某个值替换为另一个值。
std::replace(vec.begin(), vec.end(), 1, 99); // 将所有 1 替换为 99
- std::copy: 将区间内的元素复制到另一个区间。
std::copy(vec.begin(), vec.end(), vec2.begin());
6. 归并算法merge
定义:将两个有序区间合并到一个有序区间。
语法:std::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin());
其它算法:
- std::inplace_merge: 在单个区间中合并两个有序子区间。
std::inplace_merge(vec.begin(), middle, vec.end());
7. 集合算法
常用的语法:
std::set_union: 计算两个有序集合的并集。
auto it = std::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin());
- 其中返回的it是result中得到的并集元素中最后一个的下一个位置
std::set_intersection: 计算两个有序集合的交集。
auto it = std::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin());
std::set_difference: 计算集合的差集。
auto it = std::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin());
8. 其它有用算法
常用的语法:
std::accumulate(需要
库) :计算范围内元素的累计和。int sum = std::accumulate(vec.begin(), vec.end(), 0);
std::for_each: 对区间内的每个元素执行相应操作。
std::for_each(vec.begin(), vec.end(), [](int& x) { x += 1; });
std::min_element 和 std::max_element: 查找区间内的最小值和最大值。
auto min_it = std::min_element(vec.begin(), vec.end());
auto max_it = std::max_element(vec.begin(), vec.end());