✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛)
🌈 个人Motto:他强任他强,清风拂山冈!
🔥 所属专栏:C++深入学习笔记
💫 欢迎来到我的学习笔记!
一、C/C++中的字符串
1.1. C语言中的数组
在C语言中,数组是一组相同类型元素的有序集合。与字符串类型,它的大小在编译时就已经确定无法更改了。
// 大小为5的整形数组
int arr1[5] = { 1,2,3,4,5 };
// 大小为5的浮点型数组
double arr2[5] = { 3.14 };
1.2. C++中的数组
与string
类似,C++为了更加方便就引入了一个支持可动态数组大小数组的序列容器vector
。vector
的特点如下:
- 基本特性
- 存储机制
- 空间分配策略
- 与其他容器比较
- 还要注意:
//整型数组
vector<int> v1;
//浮点型数组
vector<double> v2;
vector_53">二、vector的接口
vector
的接口和string
的接口很相似。
vector_56">2.1 vector类对象的默认成员函数
构造函数声明 | 接口说明 |
---|---|
vector() | 构造函数 |
vector(size_type n,const value_type& val = value_type()) | 构造并初始化n个val |
vector(Inputlterator first, Inputilerator last) | 使用迭代器进行初始化构造 |
vector(const vector& x) | 拷贝构造 |
vector& operator=(const vector& x) | 赋值重载 |
2.1.1 构造函数
explicit vector(const allocator_type& alloc = allocator_type());
explicit vector(size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
template <class InputIterator>vector(InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
vector(const vector& x);
// 其中的 allocator 是空间配置器,只是用于分配空间,目的为增加申请释放空间的效率
// 对于vector来说是一个模板类,所以我们在实例化时要声明其内置类型。
- 无参构造函数
- 示例代码:
// 构造函数创建一个空的vector,初始大小为0
vector<int> v1;// 无参(没有传入任何的参数)
- 这种构造函数创建一个空的
vector
,其初始化大小为0。
- 指定个数和初始值构造函数
- 示例代码:
vector<int> v2(3, 2);
for (int i = 0; i < v2.size(); i++)
{cout << v2[i] << " ";
}
// 2 2 2
- 这里构造了一个包含3个元素每个元素初始值为2的
vector
。
-
使用迭代器区间构造函数
- 示例代码
string s("abcd");
vector<int> v3(s.begin(), s.end());
- 这里使用迭代器区间来构造
vector
。需要注意的是,如果vector
的内置类型与迭代器所指向的元素类型不匹配(如上述代码中v3
为vector<int>
,而s
是string
类型,会发生隐式类型转换,将字符的 ASCII 值存储到vector
中)。如果要正确存储字符,应声明为vector<char>
。 - 此外,还可以利用指针(其底层基于迭代器思维)来初始化
vector
,例如:
// 利用天然的迭代器 —— 指针进行初始化
int a[] = {1, 2, 3, 4};
vector<int> v2(a, a + 4);
2.1.2 拷贝构造函数
- 示例代码:
vector<int> v1(3, 2);
vector<int> v2(v1);// 拷贝构造
// 2 2 2
2.1.3 赋值重载
- 示例代码:
vector<int> v1(3, 2);// 创建3个2
vector<int> v2;
v2 = v1;// 赋值重载
//v3 = v1;//error:要求是同一类型的vector类
// 2 2 2
vector_148">2.2 vector类对象的访问及遍历操作
2.2.1 opeartor[] 和 at()
- operator[]
- 示例代码
#include <vector>
#include <iostream>int main()
{std::vector<int> v({ 1,2,3,4,5 });for (size_t i = 0; i < v.size(); i++){std::cout << v[i] << " ";}std::cout << std::endl;return 0;
}
// 1 2 3 4 5
operator[]
是一种常见的访问vector
元素的方式,通过下标来获取元素。- 下标越界会导致断言错误。
- at()
- 示例代码
#include <vector>
#include <iostream>int main()
{std::vector<int> v{ 1,2,3,4,5 };for (size_t i = 0; i < v.size(); i++){std::cout << v.at(i) << " ";}std::cout << std::endl;return 0;
}
at()
函数也用于访问元素,与operator[下标]
不同的是,如果下标越界,at(下标)
会抛出异常。
2.2.2 迭代器
- begin() 和 end()(正向迭代器)
- 示例代码
#include <vector>
#include <iostream>int main()
{std::vector<int> v{ 1,2,3,4,5 };std::vector<int>::iterator it = v.begin();while (it != v.end()){std::cout << *it << " "; it++;}std::cout << std::endl;return 0;
}
// 1 2 3 4 5
- rbegin() 和 rend()(反向迭代器)
- 示例代码
#include <vector>
#include <iostream>
int main()
{std::vector<int> v{ 1,2,3,4,5 };std::vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){std::cout << *rit << " ";rit++;}std::cout << std::endl;return 0;
}
// 5 4 3 2 1
2.2.3 范围for
- 示例代码
#include <vector>
#include <iostream>int main()
{std::vector<int> v{ 1,2,3,4,5 };for (auto e : v){std::cout << e << " ";}std::cout << std::endl;return 0;
}
// 1 2 3 4 5
- 范围for是一种简洁的遍历
vector
元素的方式吗,基于迭代器实现。
2.2.4 sort
vector<int> v({1,2,3,4,5});
sort(v.begin(), v.end());
for(auto e : v)
{cout<< e << " ";
}
cout << endl;
迭代器与sort概述
- 迭代器与
algorithm
头文件中的sort
函数- 在涉及迭代器时,
algorithm
头文件中的sort
函数是一个重要的库函数。它有两个重载形式。
- 在涉及迭代器时,
sort
函数的第一个重载形式:升序排列。
// 传入的仿函数对象为less<int>(),升序排列
sort(v.begin(), v.end());
sort
函数的第二个重载形式:默认升序排列- 最后一个参数是 STL 六大组件中的仿函数。
- 默认传入的仿函数对象为
less<int>()
,这是一个小堆,代表升序。 - 如果要进行降序排序,需要传入的仿函数对象为
greater<int>()
(这是一个大堆,代表降序),如sort(v.begin(), v.end(), greater<int>());
,运行结果可观察到确实进行了降序排序。
// 传入的仿函数对象为greater<int>() ,降序排列
sort(v.begin(), v.end(), greater<int>());
反向迭代器与sort
函数:
- 反向迭代器传递的可行性
- 除了正向迭代器,反向迭代器也可以传递给
sort
函数。
- 除了正向迭代器,反向迭代器也可以传递给
sort(v.rbegin(), v.rend());
- 反向迭代器排序的特性
- 当传递反向迭代器并默认按照升序排序时,从后往前进行升序排序就相当于从前往后进行降序排序。
总结:
- 升序排序(正向迭代器 - 第一个重载形式)
sort(v.begin(), v.end());
- 效果:按照默认的升序规则对`v`容器中的元素进行排序。
- 降序排序(正向迭代器 - 第二个重载形式)
sort(v.begin(), v.end(), greater<int>());
- 效果:通过传入`greater<int>()`仿函数对象,实现对`v`容器中的元素进行降序排序。
- 反向迭代器排序
sort(v.rbegin(),v.rend());
- 效果:从后往前进行升序排序,等同于从前往后进行降序排序。
vector_330">2.3 vector类对象的常见容量操作
2.3.1 size 和 capacity
- size
- 示例代码
#include <vector>
#include <iostream>
int main()
{std::vector<int> v(10, 1);std::cout << "v的大小:" << v.size() << std::endl;return 0;
}
// v的大小:10
size
函数返回vector
中当前元素的个数。
- capacity
- 示例代码:
#include <vector>
#include <iostream>int main()
{std::vector<int> v;std::cout << "初始容量: " << v.capacity() << std::endl;for (int i = 0; i < 10; i++){v.push_back(i);}std::cout << "添加10个元素后的容量: " << v.capacity() << std::endl;return 0;
}
// 初始容量: 0
// 添加10个元素后的容量: 13
capacity
函数返回vector
当前分配的存储空间能够容纳的元素数量。在不同的 STL 版本中,vector
的扩容机制有所不同。- 例如在VS下的扩容机制是呈现 1.5 进行增长的,其STL是
P.J.版本
;在 Linux 下却始终是呈现的一个2倍的扩容机制,其STL是SGI版本
2.3.2 empty
- 示例代码:
#include <vector>
#include <iostream>int main()
{std::vector<int> v;std::cout << "初始时是否为空: " << (v.empty() ? "是" : "否") << std::endl;v.push_back(1);std::cout << "添加元素后是否为空: " << (v.empty() ? "是" : "否") << std::endl;return 0;
}
// 初始时是否为空: 是
// 添加元素后是否为空: 否
2.3.3 reserve 和 resize
-
reserve
- 示例代码:
#include <vector>
#include <iostream>int main()
{std::vector<int> v;v.reserve(10);std::cout << "预留空间后的容量: " << v.capacity() << std::endl;return 0;
}
// 预留空间后的容量: 10
- 示例代码2:
void TestVectorExpandOP()
{vector<int> v;size_t sz = v.capacity();v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容cout << "making bar grow:\n";for (int i = 0; i < 100; ++i){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}
reserve
函数用于预先分配指定大小的存储空间,以避免在后续添加元素时频繁扩容。需要注意的是,reserve
只改变vector
的容量,不改变元素个数(size
)。
- resize
- 示例代码:
#include <vector>
#include <iostream>int main()
{std::vector<int> v;v.resize(3);std::cout << "调整大小后的元素个数: " << v.size() << std::endl;for (int i = 0; i < v.size(); i++) {std::cout << v[i] << " ";}std::cout << std::endl;return 0;
}
// 调整大小后的元素个数: 3
// 0 0 0
resize
函数用于改变vector
的元素个数。- 如果新的大小大于当前大小,则会在末尾添加默认值(对于基本类型,默认值为 0)的元素;
- 如果新的大小小于当前大小,则会删除多余的元素。
vector_462">2.4 vector类对象的修改操作
函数名称 | 功能 |
---|---|
push_back | 在数组后追加元素 |
insert | 在指定位置追加元素 |
assign | 使用指定数组替换原数组 |
pop_back | 删除数组最后一个元素 |
erase | 删除数组指定部分区间 |
swap | 交换两个数组 |
2.4.1 push_back 和 pop_back
- push_back
- 示例代码:
#include <vector>
#include <iostream>
#include <string>int main()
{std::vector<std::string> v;std::string name1("张三");v.push_back(name1);v.push_back(std::string("李四"));v.push_back("王五");for (auto str : v) {std::cout << str << " ";}std::cout << std::endl;return 0;
}
// 张三 李四 王五
push_back
函数用于在vector
的末尾添加一个元素。它支持多种方式添加元素,如添加已有的对象、匿名对象或者利用单参数构造函数引发的隐式类型转换来添加元素。
- pop_back
- 示例代码:
#include <vector>
#include <iostream>int main()
{std::vector<int> v = { 1, 2, 3 };v.pop_back();for (auto num : v) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
// 1 2 (没有3)
pop_back
函数用于删除vector
末尾的一个元素。
2.4.2 insert 和 erase
- insert
- 示例代码:
#include <vector>
#include <iostream>int main()
{std::vector<std::string> v;v.insert(v.begin(), "刘琦");v.push_back("赵六");for (auto str : v) {std::cout << str << " ";}std::cout << std::endl;return 0;
}
// 刘琦 赵六
insert
函数用于在指定位置之前插入一个元素。它有多种重载形式,可以根据不同的需求在vector
中的不同位置插入元素。
- erase
- 示例代码:
#include <vector>
#include <iostream>int main()
{std::vector<int> v = { 1, 2, 3, 4 };v.erase(v.begin() + 1);for (auto num : v) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
// 1 3 5
erase
函数用于删除指定位置的元素。它有两种重载形式,一种是传递单个迭代器来删除指定位置的元素,另一种是传递迭代器区间来删除多个元素。在想要删除容器内指定数据时,如果仅依靠vector
本身的接口是无法直接做到的,需要借助<algorithm>
头文件中的find
函数。
2.4.3 find函数与迭代器失效
- 在
vector
中,查找指定元素通常使用<algotithm>
头文件中的find
函数。 - 示例代码:
#include <vector>
#include <iostream>
#include <algorithm>int main()
{std::vector<int> v = { 1, 2, 3, 1, 4 };auto pos = std::find(v.begin(), v.end(), 1);while (pos != v.end()) {v.erase(pos);pos = std::find(pos, v.end(), 1);}for (auto num : v) {std::cout << num << " ";}return 0;
}
- 在这个例子中,当我们使用
erase
函数删除元素后,vector
的内部结构可能会发生改变,导致之前获取的迭代器pos
失效。如果继续使用失效的迭代器,会导致未定义行为,如程序崩溃或者产生错误的结果。为了避免这种情况,在删除元素后,需要重新获取有效的迭代器,如代码中在每次删除元素后重新调用find
函数获取下一个匹配元素的迭代器。