文章目录
一、 vector的使用
1.vector的建造
创建 std::vector 的不同方式
// constructing vectors
#include <iostream>
#include <vector>
int main()
{
// constructors used in the same order as described above:
// 空的 int 类型向量
std::vector<int> first;
// 创建了一个名为 second 的向量,包含4个元素,每个元素的值都是100。
std::vector<int> second(4, 100);
// 迭代器范围构造函数
// 创建了一个名为 third 的向量,它通过遍历 second 的元素构造而成,因此 third 的内容与 second 相同。
std::vector<int> third(second.begin(), second.end());
// 拷贝构造函数
// 创建了一个名为 fourth 的向量,它是 third 向量的副本,因此 fourth 的内容与 third 相同。
std::vector<int> fourth(third);
// 通过数组的首地址 myints 和末地址 myints + sizeof(myints) / sizeof(int) 构造了一个名为 fifth 的向量。
int myints[] = {16, 2, 77, 29};
std::vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));
std::cout << "The contents of fifth are:";
for (std::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
std::cout << ' ' << *it; //*it 解引用迭代器,获取当前元素的值
// endl 是一个操纵符,它的作用是向输出流插入一个换行符,并刷新缓冲区。
std::cout << '\n';
return 0;
}
2.vector iterator 的使用
// vector::begin/end
#include <iostream>
#include <vector>
int main ()
{
std::vector<int> myvector;
for (int i=1; i<=5; i++) myvector.push_back(i);
std::cout << "myvector contains:";
for (std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector(5); // 创建一个包含5个默认值的int类型向量,每个元素初始值为0
int i = 0; // 定义并初始化一个整型变量i为0
std::vector<int>::reverse_iterator rit = myvector.rbegin();
// 定义一个反向迭代器rit,初始化为指向myvector的反向起始位置(即最后一个元素)
for (; rit != myvector.rend(); ++rit) // 使用反向迭代器遍历myvector,从最后一个元素到第一个元素
*rit = ++i; // 将当前迭代器指向的元素赋值为i递增后的值(即1到5)
std::cout << "myvector contains:";
for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)
// 使用正向迭代器遍历myvector,从第一个元素到最后一个元素
std::cout << ' ' << *it; // 输出当前元素的值,前面加一个空格
std::cout << '\n';
return 0;
}
3.vector 空间增长问题
- size 获取数据个数
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myints; // 创建一个空的int类型向量
std::cout << "0. size: " << myints.size() << '\n';
// 输出当前向量的大小,应该是0,因为向量刚刚创建,还没有添加任何元素
for (int i = 0; i < 10; i++)
myints.push_back(i);
// 使用for循环向向量中添加10个元素,这些元素的值分别为0到9
std::cout << "1. size: " << myints.size() << '\n';
myints.insert(myints.end(), 10, 100);
// 在向量的末尾插入10个值为100的元素
std::cout << "2. size: " << myints.size() << '\n';
// 输出当前向量的大小,应该是20,因为刚刚插入了10个新元素
myints.pop_back();
// 删除向量的最后一个元素
std::cout << "3. size: " << myints.size() << '\n';
// 输出当前向量的大小,应该是19,因为刚刚删除了1个元素
return 0; // 返回0,表示程序成功结束
}
- capacity
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector; // 创建一个空的int类型向量
// 在向量中设置一些内容:
for (int i = 0; i < 100; i++)
myvector.push_back(i);
// 使用for循环向向量中添加100个元素,这些元素的值为0到99
std::cout << "size: " << (int)myvector.size() << '\n';
// 输出当前向量的大小,表示向量中元素的个数,应该是100
std::cout << "capacity: " << (int)myvector.capacity() << '\n';
// 输出当前向量的容量,表示向量在不重新分配内存的情况下可以容纳的元素数量
std::cout << "max_size: " << (int)myvector.max_size() << '\n';
// 输出当前向量的最大可能大小,表示向量在理论上能够容纳的最大元素数量
return 0; // 返回0,表示程序成功结束
}
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
- empty
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector;
int sum(0);
for (int i = 1; i <= 10; i++)
myvector.push_back(i);
while (!myvector.empty())
// 使用while循环,持续执行直到向量为空
{
sum += myvector.back();
// 将向量末尾的元素值加到sum中
myvector.pop_back();
// 删除向量末尾的元素
}
std::cout << "total: " << sum << '\n';
return 0;
}
- resize
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector;
for (int i = 1; i < 10; i++)
myvector.push_back(i);
// 使用for循环向向量中添加1到9的整数,这些元素依次添加到向量的末尾
myvector.resize(5);
// 将向量的大小调整为5,超过5的元素将被删除。保留前5个元素:1, 2, 3, 4, 5
myvector.resize(8, 100);
// 将向量的大小调整为8,新增加的元素初始化为100。因此,向量现在包含:1, 2, 3, 4, 5, 100, 100, 100
myvector.resize(12);
// 将向量的大小调整为12,新增的元素用默认值0进行填充。向量现在包含:1, 2, 3, 4, 5, 100, 100, 100, 0, 0, 0, 0
std::cout << "myvector contains:";
for (int i = 0; i < myvector.size(); i++)
// 使用for循环遍历向量的所有元素
std::cout << ' ' << myvector[i];
std::cout << '\n';
return 0;
}
resize在开空间的同时还会进行初始化,影响size。
- reserve
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
#include <iostream>
#include <vector>
int main()
{
std::vector<int>::size_type sz; // 定义一个变量sz,用于存储向量的容量大小
std::vector<int> foo; // 创建一个空的int类型向量foo
sz = foo.capacity(); // 获取foo当前的容量并存储到sz中
std::cout << "making foo grow:\n";
for (int i = 0; i < 100; ++i)
{
foo.push_back(i); // 将整数i依次添加到foo中
if (sz != foo.capacity())
{
sz = foo.capacity(); // 如果foo的容量发生变化,则更新sz
std::cout << "capacity changed: " << sz << '\n';
}
}
std::vector<int> bar; // 创建一个空的int类型向量bar
sz = bar.capacity();
bar.reserve(100); // 预先将bar的容量设置为100
std::cout << "making bar grow:\n";
for (int i = 0; i < 100; ++i)
{
bar.push_back(i); // 将整数i依次添加到bar中
if (sz != bar.capacity())
{
sz = bar.capacity(); // 如果bar的容量发生变化,则更新sz
std::cout << "capacity changed: " << sz << '\n';
}
}
return 0;
}
output:
making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128
making bar grow:
capacity changed: 100
原因:
4.vector 增删查改
- push_back
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector; // 定义一个整数类型的 vector 容器 myvector,用于存储整数
int myint;
std::cout << "Please enter some integers (enter 0 to end):\n";
do
{
std::cin >> myint; // 从标准输入读取一个整数并存储在 myint 中
myvector.push_back(myint); // 将读取的整数添加到 myvector 容器的末尾
} while (myint);
// 循环直到用户输入 0,0 作为结束标志,不再添加到 myvector 中
std::cout << "myvector stores " << int(myvector.size()) << " numbers.\n";
return 0;
}
- pop_back
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector; // 定义一个存储整数的vector对象myvector
int sum(0);
myvector.push_back(100); // 向myvector的末尾添加整数100
myvector.push_back(200);
myvector.push_back(300);
while (!myvector.empty()) // 当myvector不为空时,进入循环
{
sum += myvector.back(); // 取出myvector末尾的元素并加到sum中
myvector.pop_back(); // 删除myvector末尾的元素
}
std::cout << "The elements of myvector add up to " << sum << '\n'; // 输出myvector中所有元素的和
return 0;
}
- find
查找。(注意这个是算法模块实现,不是vector的成员接口)
返回值指向范围中比较等于 val 的第一个元素的迭代器。如果没有元素匹配,函数返回最后一个
#include <iostream>
#include <algorithm> // std::find
#include <vector>
int main()
{
int myints[] = {10, 20, 30, 40}; // 定义一个整数数组 myints,包含 4 个元素
int *p; // 定义一个指针 p,用于存储找到的元素的地址
// 在数组 myints 中查找值为 30 的元素,返回指向该元素的指针
p = std::find(myints, myints + 4, 30);
// 如果找到的指针 p 不是数组末尾的地址,则说明找到了元素
if (p != myints + 4)
std::cout << "Element found in myints: " << *p << '\n'; // 输出找到的元素值
else
std::cout << "Element not found in myints\n";
// 用 myints 数组的元素初始化一个 vector 对象 myvector
std::vector<int> myvector(myints, myints + 4);
std::vector<int>::iterator it;
it = find(myvector.begin(), myvector.end(), 30);
// 如果找到的迭代器 it 不是 vector 的末尾,则说明找到了元素
if (it != myvector.end())
std::cout << "Element found in myvector: " << *it << '\n';
else
std::cout << "Element not found in myvector\n";
return 0;
}
- insert 在position之前插入val
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector(3, 100); // 定义一个包含3个元素的vector对象myvector,每个元素值为100
std::vector<int>::iterator it;
it = myvector.begin();
it = myvector.insert(it, 200); // 在myvector的第一个位置插入值为200的元素,it更新为指向新插入的元素
myvector.insert(it, 2, 300); // 在it位置之前插入两个值为300的元素
// "it"指向的元素位置已经无效,重新获取一个指向vector开头的迭代器
it = myvector.begin();
// 定义一个包含2个元素的vector对象anothervector,每个元素值为400
std::vector<int> anothervector(2, 400);
myvector.insert(it + 2, anothervector.begin(), anothervector.end()); // 在myvector中第3个位置插入anothervector的所有元素
int myarray[] = {501, 502, 503}; // 定义一个包含3个元素的数组myarray
myvector.insert(myvector.begin(), myarray, myarray + 3); // 将myarray的所有元素插入到myvector的开头
std::cout << "myvector contains:";
for (it = myvector.begin(); it < myvector.end(); it++)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
- erase 删除position位置的数据
指向函数调用删除的最后一个元素之后的元素的新位置的迭代器。如果操作删除了序列中的最后一个元素,则指向容器末端
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector; // 定义一个空的整数vector对象myvector
// 设置一些值(从1到10)
for (int i = 1; i <= 10; i++)
myvector.push_back(i);
// 删除第6个元素
myvector.erase(myvector.begin() + 5); // 删除myvector中的第6个元素(索引为5)
// 删除前3个元素
myvector.erase(myvector.begin(), myvector.begin() + 3); // 删除myvector中的前3个元素
std::cout << "myvector contains:";
for (unsigned i = 0; i < myvector.size(); ++i)
std::cout << ' ' << myvector[i];
std::cout << '\n';
return 0;
}
- swap
// swap vectors
#include <iostream>
#include <vector>
int main()
{
std::vector<int> foo(3, 100); // three ints with a value of 100
std::vector<int> bar(5, 200); // five ints with a value of 200
foo.swap(bar);
std::cout << "foo contains:";
for (unsigned i = 0; i < foo.size(); i++)
std::cout << ' ' << foo[i];
std::cout << '\n';
std::cout << "bar contains:";
for (unsigned i = 0; i < bar.size(); i++)
std::cout << ' ' << bar[i];
std::cout << '\n';
return 0;
}
- operator[]
#include <iostream>
#include <vector>
int main()
{
std::vector<int> myvector(10); // 定义一个包含10个元素的vector对象myvector,所有元素都初始化为0
std::vector<int>::size_type sz = myvector.size();
// 赋值一些数值:
for (unsigned i = 0; i < sz; i++)
myvector[i] = i; // 使用下标运算符[]给myvector中的每个元素赋值,使其值等于其索引
// 使用下标运算符[]反转vector:
for (unsigned i = 0; i < sz / 2; i++) // 只需遍历前一半元素
{
int temp; // 定义一个临时变量temp
temp = myvector[sz - 1 - i]; // 将myvector末尾的元素赋值给temp
myvector[sz - 1 - i] = myvector[i]; // 将myvector前半部分的元素值赋给对应的末尾位置
myvector[i] = temp; // 将temp中的值(原末尾元素)赋给myvector的前半部分
}
std::cout << "myvector contains:";
for (unsigned i = 0; i < sz; i++)
std::cout << ' ' << myvector[i];
std::cout << '\n';
return 0;
}
5.vector 迭代器失效问题*
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对
指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器
底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即
如果继续使用已经失效的迭代器,程序可能会崩溃)。
- 插入元素导致迭代器失效:
当向 vector 中插入元素时,特别是当插入的位置不是 vector 的末尾时,vector 可能需要重新分配内存。这是因为 vector 是基于数组实现的,如果插入操作导致 vector 的容量不足,vector 会分配一块新的更大的内存区域,并将现有元素复制到新的位置。
迭代器失效的原因:
- 如果 vector 重新分配了内存,那么所有指向旧内存的迭代器都会失效。
- 如果在中间插入元素,插入位置后的所有迭代器都会失效,因为这些元素的位置可能会发生改变。
避免方法:
- 插入元素后重新获取迭代器。
- 避免在遍历 vector 时进行插入操作。
- 删除元素导致迭代器失效
当从 vector 中删除元素时,vector 中后续的元素会向前移动以填补被删除元素留下的空位。这会导致某些迭代器失效。
迭代器失效的原因:
- 如果删除的元素不是最后一个元素,则删除位置之后的所有迭代器都会失效,因为这些元素的位置改变了。
避免方法:
- 在删除操作后重新获取迭代器。
- 使用返回的有效迭代器(erase 操作返回的是指向被删除元素的下一个元素的迭代器)。
- 容量变化导致的迭代器失效
vector 的容量是动态增长的,每次插入新元素时,如果当前容量不足以容纳新的元素,vector 会重新分配一块更大的内存并复制现有元素。
迭代器失效的原因:
- 每次重新分配内存后,所有迭代器都会失效,因为它们仍指向旧的内存位置。
避免方法:- 可以通过调用 reserve() 方法提前分配足够的空间,以避免不必要的内存重新分配。
- 举例
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{1, 2, 3, 4, 5, 6};
auto it = v.begin();
v.assign(100, 8);
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
在执行 v.assign(100, 8); 之后,v 中的元素全部变为100个值为8的元素,并且这些元素会被存储在一个新的内存区域。因此,原来的迭代器 it 不再指向有效的内存位置。接下来在 while 循环中使用这个失效的迭代器会导致未定义行为。
修改代码
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{1, 2, 3, 4, 5, 6};
v.assign(100, 8);
auto it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
- 举例2
#include <iostream>
using namespace std;
#include <vector>
int main()
{
int a[] = {1, 2, 3, 4};
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据,导致pos迭代器失效。
v.erase(pos);
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
在 C++ 中,erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理
论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。
修改:
#include <iostream>
using namespace std;
#include <vector>
int main()
{
int a[] = {1, 2, 3, 4};
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据,并返回指向下一个有效元素的迭代器
if (pos != v.end())
{
pos = v.erase(pos);
}
// 在检查pos是否为有效的结束位置后访问元素
if (pos != v.end())
{
cout << *pos << endl; // 输出删除后的位置的新元素
}
else
{
cout << "No more elements in the vector." << endl; // 如果已到达end,则提示
}
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
- 举例3
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{1, 2, 3, 4};
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;
}
return 0;
}
迭代器失效:在 std::vector 中,当你调用 erase 删除元素时,所有指向被删除元素以及其后元素的迭代器都会失效。换句话说,删除操作会导致当前迭代器 it 失效。失效的迭代器如果继续使用,可能会导致未定义行为,程序可能崩溃或输出错误的结果。
修改:
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{1, 2, 3, 4};
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);
else
++it;
}
return 0;
}
在 erase 操作之后,erase 方法返回一个新的有效迭代器,该迭代器指向被删除元素之后的元素。通过将 it 赋值为这个新迭代器,可以确保后续的操作是基于有效的迭代器进行的。
二、vector深度剖析及模拟实现
1.使用memcpy拷贝问题
如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为
memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。
2.二维动态数组理解
vector 是动态数组
而 vector<vector< int > > 就是一个存储 vector< int > 的 vector,也就是说它是一个二维的动态数组结构
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class Solution
{
public:
vector<vector<int>> generate(int numRows) // 定义一个成员函数 generate,返回值类型是二维整数向量,参数是整数 numRows
{
vector<vector<int>> vv(numRows); // 声明一个二维向量 vv,大小为 numRows,用于存储帕斯卡三角形的行
for (int i = 0; i < numRows; ++i) // 遍历每一行
{
vv[i].resize(i + 1, 1); // 将当前行的大小设为 i+1,并初始化所有元素为1
}
for (int i = 2; i < numRows; ++i) // 从第三行开始遍历每一行
{
for (int j = 1; j < i; ++j) // 遍历当前行的每个元素(除了第一个和最后一个)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1]; // 设置当前元素为上一行左边和上边元素的和
}
}
return vv; // 返回构造好的帕斯卡三角形
}
};