本篇文章进行STL中vector序列容器的学习!!!
【vector文档】
(constructor)函数名称 | 功能说明 |
---|---|
vector() (重点) | 无参构造函数,有效数据和容量为0 |
vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
vector (const vector& v) (重点) | 拷贝构造函数 |
vector (InputIterator first, InputIterator last) | 使用迭代器进行初始化构造 |
举个栗子:
void Testvector()
{
vector<int> v1; // 默认构造函数
vector<int> v2(10, 100); // 构造十个有效数据并且初始化为100
vector<int> v3(v2.begin(), v2.end()); // 使用迭代器进行构造
vector<int> v4(v3); // 拷贝构造函数
//下面涉及迭代器部分知识,后面会将到
int Array[]{ 0, 1, 2, 3, 4 };
//使用迭代器进行初始化,迭代器本质是一个指针 --- [Array, Array + size)
vector<int> v5(Array, Array + sizeof(Array) / sizeof(int));
//使用迭代器进行输出
for (vector<int>::iterator It = v5.begin(); It != v5.end(); ++It)
cout << *It << ' ';
cout << endl;
}
iterator的使用 | 接口说明 |
---|---|
begin + end(重点) | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin + rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator |
void TestvectorIterator()
{
vector<int> v;
for (int i = 0; i < 5; ++i)
// 使用尾插接口插入数据 --- {0, 1, 2, 3, 4}
v.push_back(i);
// 使用迭代器进行遍历打印
vector<int>::iterator It = v.begin();
while (It != v.end())
{
cout << *It << ' ';
++It;
}
cout << endl;
// 使用迭代器对容器中的数据进行修改
It = v.begin();
while (It != v.end())
{
// 每次增大二倍
*It *= 2;
++It;
}
// 使用反向迭代器进行遍历
vector<int>::reverse_iterator rIt = v.rbegin();
while (rIt != v.rend())
{
cout << *rIt << ' ';
++rIt;
}
cout << endl;
}
容量空间 | 接口说明 |
---|---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断容器是否为空 |
resize(重点) | 改变vector的size |
reserve(重点) | 改变vector的capacity |
举个栗子1️⃣:
// 未提前开辟空间版本 --- 测试capacity、reserve接口
void Testvectorcapacity1()
{
vector<int> v;
size_t size = v.capacity();
cout << "开始尾插数据: \n";
for (int i = 0; i < 100; ++i)
{
v.push_back(i);
if (size != v.capacity())
{
size = v.capacity();
cout << "容量发生改变: " << size << endl;
}
}
}
// 提前开辟空间版本
void Testvectorcapacity2()
{
vector<int> v;
// 提取开辟好100个空间,提高效率,防止频繁发生扩容
v.reserve(200);
size_t size = v.capacity();
cout << "开始尾插数据: \n";
for (int i = 0; i < 100; ++i)
{
v.push_back(i);
if (size != v.capacity())
{
size = v.capacity();
cout << "容量发生改变: " << size << endl;
}
}
}
举个栗子2️⃣:
//测试size、resize接口
void Testvectorresize()
{
vector<int> v;
// 通过尾插接口插入数据 --- {1 ~ 10}
for (int i = 1; i < 10; i++)
v.push_back(i);
// 将有效数据缩减到5个
v.resize(5);
//将有效数据增加到8个,多出的位置初始化为100
v.resize(8, 100);
//将有效数据增加到12个,多出的位置默认初始化为0
v.resize(12);
cout << "v contains:";
for (int i = 0; i < v.size(); i++)
cout << ' ' << v[i];
cout << '\n';
}
vector增删查改 | 接口说明 |
---|---|
push_back(重点) | 尾插 |
pop_back(重点) | 尾删 |
find | 查找(注意这个是算法模块实现,不是vector的成员接口) |
insert | 在position之前插入val |
erase | 删除position位置的数据 |
swap | 交换二个vector的数据空间 |
operator[](重点) | 像数组一样通过下标进行访问 |
注意:这里的尾插、尾删、insert 和 erase接口跟数据结构中的动态顺序表一样
举个栗子1️⃣:
//测试push_back、pop_back接口
void Testvector()
{
vector<int> v;
for (int i = 0; i < 5; ++i)
// 尾插数据
v.push_back(i);
for (auto It = v.begin(); It != v.end(); ++It)
cout << *It << ' ';
cout << endl;
//尾删数据
v.pop_back();
v.pop_back();
for (auto It = v.begin(); It != v.end(); ++It)
cout << *It << ' ';
cout << endl;
}
举个栗子2️⃣:
//测试find、insert、erase接口
void Testvector()
{
int Array[]{1, 2, 3, 4};
vector<int> v(Array, Array + sizeof(Array) / sizeof(int));
//使用find查找3所在的位置的iteration(地址)
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
//在pos位置左边插入数据100
v.insert(pos, 100);
for (auto It = v.begin(); It != v.end(); ++It)
cout << *It << ' ';
cout << endl;
pos = find(v.begin(), v.end(), 3);
//删除pos位置的数据
v.erase(pos);
for (auto It = v.begin(); It != v.end(); ++It)
cout << *It << ' ';
cout << endl;
}
举个栗子3️⃣:
//测试swap、operator[]接口
void Testvector()
{
int Array[]{ 1, 2, 3, 4 };
vector<int> v(Array, Array + sizeof(Array) / sizeof(int));
//通过[]进行读写操作
v[0] = 10;
//通过[]进行遍历
for (int i = 0; i < v.size(); ++i)
cout << v[i] << ' ';
cout << endl;
vector<int> v2;
//交换二个vector的空间
v2.swap(v);
for (auto It = v2.begin(); It != v2.end(); ++It)
cout << *It << ' ';
cout << endl;
//C++11支持的新式范围for循环遍历 --- 这里没有输出,因为交换的是空的vector
for (auto e : v)
cout << e << ' ';
cout << endl;
}
注意:
迭代器的作用:
比如:
对于vector可能会导致其迭代器失效的操作有:
void Testvector()
{
// 使用初始化列表进行初始化
vector<int> v{ 1, 2, 3, 4, 5 };
auto It = v.begin();
// 将v元素个数增加到100个,多出的位置使用value填充,操作期间底层会扩容
v.resize(10, 8);
// reserse的作用是改变扩容大小,但不改变有效数据个数,操作期间可能会引起底层容量的改变(扩容)
v.resize(100);
// 插入元素期间,也可能会引起扩容,而导致原空间被释放
v.insert(v.begin(), 100);
v.push_back(100);
// 给vector重新赋值,也可能引起底层容量的改变
v.assign(100, 8);
}
出错原因:
解决方式:
void Testvector()
{
// 使用初始化列表进行初始化
vector<int> v{ 1, 2, 3, 4, 5 };
auto It = v.begin();
v.resize(100, 8); // 发生扩容,迭代器It失效
// 对迭代器重新初始化,拿到有效的迭代器
It = begin();
while (It != v.end())
{
cout << *It << " ";
++It;
}
cout << endl;
}
void Testvector()
{
vector<int> v{ 1, 2, 3, 4 };
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 删除pos位置的值后,导致迭代器失效
v.erase(pos);
//此处为非法访问,会崩溃
cout << *pos << endl;
}
程序解析:
void Testvector()
{
vector<int> v{ 1, 2, 3, 4 };
auto It = v.begin();
while (It != v.end())
{
// 如果迭代器中的值尾2的倍数,则删除它
if (*It % 2 == 0)
It = v.erase(It); // 删除后会导致迭代器失效,应该重新赋值(erase返回一个有效迭代器)
else
++It;
}
}
程序解析:
解决方案:迭代器失效解决办法:在使用前,对迭代器重新赋值即可
vector.h
#ifndef VECTOR_H_
#define VECTOR_H_
#include "reverse_iterator.h"
#include
#include
#include
#include
using namespace std;
namespace myvector
{
template <typename T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef Reverse_Iterator<iterator> reverse_iterator;
typedef Reverse_Iterator<const_iterator> const_reverse_iterator;
//======================== 正向 iterator ======================================
iterator begin() { return start; }
iterator end() { return finish; }
const_iterator cbegin() const { return start; }
const_iterator cend() const { return finish; }
//======================== 反向 iterator ======================================
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
//======================== capacity ======================================
size_t size() const { return size_t(finish - start); }
size_t capacity() const { return size_t(end_of_storage - start); }
bool empty() const { return start == finish; }
//======================== constructor ======================================
vector()
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{}
vector(int n, const T& val = T())
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{
reserve(n);
while (n--)
insert(finish, val);
}
template <typename InputIterator>
vector(InputIterator first, InputIterator last)
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{
reserve(last - first);
while (first != last)
{
insert(finish, *first);
++first;
}
}
vector(const vector<T>& v)
: start(nullptr)
, finish(nullptr)
, end_of_storage(nullptr)
{
// 通过区间构造来进行对v的拷贝
vector<T> tmp(v.cbegin(), v.cend());
this->swap(tmp);
}
vector<T>& operator=(vector<T> v)
{
this->swap(v);
return *this;
}
~vector()
{
if (start)
{
delete[] start;
start = finish = end_of_storage = nullptr;
}
}
//======================== Expansion and Adjustment ======================================
void reserve(size_t n)
{
// 首先保存old有效数据的数量
size_t sz = size();
if (n > capacity())
{
iterator tmp = new T[n];
if (start != nullptr)
{
// 这里不能使用memcpy,会导致值拷贝,如果vector实例化类型为vector,值拷贝会导致程序崩溃
// 需要进行深拷贝,使用stl中的copy算法或for循环逐个拷贝
copy(start, finish, tmp);
delete[] start;
}
start = tmp;
}
// 当finish更新时,如果加的是size()可能崩溃,前提是扩容后start发生改变,new有效数据个数无法计算(pos变成野指针)
finish = start + sz;
end_of_storage = start + n;
}
void resize(size_t n, const T& val = T())
{
// n大于容量,则扩容
if (n > capacity())
reserve(n);
// n大于有效数据个数,则对扩容后的空间进行初始化为val
if (n > size())
{
while (finish < end_of_storage)
{
*finish = val;
++finish;
}
}
else { // n小于有效数据个数,则减少有效数据
finish = start + n;
}
}
//======================== Tail insertion and tail deletion ======================================
void push_back(const T& val)
{
insert(finish, val);
}
void pop_back()
{
if (finish > start)
--finish;
else
cout << "Tail deletion failed, Error: array access out of bounds!!!\n";
}
//======================== Access value by subscript ======================================
T& operator[](size_t n)
{
if (finish < start + n && n < 0) {
cout << "Error: Array access out of bounds!!!\n";
exit(EXIT_FAILURE);
}
else
return start[n];
}
const T& operator[](size_t n) const
{
if (finish < start + n || n < 0) {
cout << "Error: Array access out of bounds!!!\n";
exit(EXIT_FAILURE);
}
else
return start[n];
}
//======================== element manipulation ======================================
iterator insert(iterator pos, const T& val)
{
assert(pos <= finish && pos >= start);
if (finish == end_of_storage)
{
// 扩容会导致迭代器pos失效,需重置
size_t pos_index = pos - start;
size_t newCapacity = (start == nullptr) ? 4 : capacity() * 2;
reserve(newCapacity);
// 重置迭代器pos
pos = start + pos_index;
}
// 插入数据
iterator end = finish - 1;
while (pos <= end)
{
*(end + 1) = *end;
--end;
}
*pos = val;
++finish;
// 返回新的迭代器,防止迭代器失效问题
return pos;
}
iterator erase(iterator pos)
{
// 删除数据会导致迭代器失效,需重置
size_t pos_index = pos - start;
iterator begin = pos;
while (begin < finish)
{
*(begin) = *(begin + 1);
++begin;
}
pos = start + pos_index;
--finish;
// erase返回pos后一个元素的位置
return pos;
}
//======================== Other methods ======================================
T& front() { return *(begin()); }
T& back() { return *(end() - 1); }
void clear()
{
finish = start;
}
void swap(vector<T>& v)
{
std::swap(start, v.start);
std::swap(finish, v.finish);
std::swap(end_of_storage, v.end_of_storage);
}
private:
iterator start; // 表示目前使用空间的头
iterator finish; // 表示目前使用空间的尾
iterator end_of_storage; // 表示目前可用空间的尾
};
};
#endif
reverse_iterator.h
- 这里的反向迭代器实现使用是迭代器萃取技术,需要对模板特化的技术的了解,可以省略
#ifndef REVERSE_ITERATOR_H_
#define REVERSE_ITERATOR_H_
namespace myvector
{
template <typename Iterator>
struct Iterator_traits
{
typedef typename Iterator::reference reference;
typedef typename Iterator::pointer pointer;
};
template <typename T>
struct Iterator_traits<T*>
{
typedef T& reference;
typedef T* pointer;
};
template <typename T>
struct Iterator_traits<const T*>
{
typedef const T& reference;
typedef const T* pointer;
};
template <typename Iterator>
struct Reverse_Iterator
{
typedef typename Iterator_traits<Iterator>::reference reference;
typedef typename Iterator_traits<Iterator>::pointer pointer;
public:
typedef Reverse_Iterator<Iterator> self;
public:
//========================== constructe =====r=============================
Reverse_Iterator(Iterator It = Iterator())
: _It(It)
{}
Reverse_Iterator(const self& rit)
: _It(rit._It)
{}
//========================== operator =====================================
reference operator*()
{
Iterator tmp(_It);
return *(--tmp);
}
pointer operator->()
{
return &(this->operator*());
}
//========================== operator =====================================
self& operator++()
{
--_It;
return *this;
}
self operator++(int)
{
Reverse_Iterator tmp(*this);
--_It;
return tmp;
}
self& operator--()
{
++_It;
return *this;
}
self operator--(int)
{
Reverse_Iterator tmp(*this);
++_It;
return tmp;
}
bool operator!=(const self& s) { return _It != s._It; }
bool operator==(const self& s) { return _It == s._It; }
//========================== 成员数据 =====================================
public:
Iterator _It;
};
}
#endif
假设模拟实现的vector中的reverse接口中,使用memcpy进行拷贝,会发生什么?
#include "vector.h"
int main()
{
myvector::vector<string> v;
v.push_back("1111");
v.push_back("2222");
v.push_back("3333");
return 0;
}
问题分析:
结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃
#include "vector.h"
#include
using namespace std;
#include "vector.hpp"
#include
using namespace std;
//构造函数
void TestVector1()
{
// 对构造函数进行测试
myvector::vector<int > v1;
myvector::vector<int> v2(5, 100);
myvector::vector<int> v3(v2.begin(), v2.end());
// 迭代器构造函数也可用于从数组构造:
int Array[] = { 1, 2, 3, 4 };
myvector::vector<int> v4(Array, Array + sizeof(Array) / sizeof(int));
cout << "使用迭代器对v4进行遍历: ";
for (myvector::vector<int>::iterator It = v4.begin(); It != v4.end(); ++It)
cout << *It << ' ';
cout << endl;
//----------------------------------------------------------------------------------------
// 测试模板类型参数为自定义类型并且涉及资源管理时,拷贝的问题
myvector::vector<string> strv;
strv.push_back("1111");
strv.push_back("2222");
strv.push_back("3333");
strv.push_back("4444");
cout << "使用下标访问进行遍历: ";
for (int i = 0; i < strv.size(); ++i)
cout << strv[i] << ' ';
cout << endl;
}
// 迭代器iteration
void PrintVector(const myvector::vector<int>& v)
{
// 使用const迭代器进行遍历打印
cout << "使用常量迭代器进行遍历: ";
myvector::vector<int>::const_iterator cIt = v.cbegin();
while (cIt != v.cend())
{
cout << *cIt << ' ';
++cIt;
}
cout << endl;
}
void TestVector2()
{
// 使用pusk_back插入4个数据
myvector::vector<int> v;
for (int i = 1; i < 5; ++i)
v.push_back(i);
PrintVector(v);
// 使用迭代器进行vector的修改
auto It = v.begin();
while (It != v.end())
{
*It *= 2;
++It;
}
PrintVector(v);
// 使用C++11的范围for循环进行打印
cout << "使用范围for循环进行遍历: ";
for (auto e : v)
cout << e << ' ';
cout << endl;
}
// 测试find、insert、erase接口
void TestVector3()
{
int Array[] = { 1, 2, 3, 4 };
myvector::vector<int> v(Array, Array + sizeof(Array) / sizeof(int));
// 使用find查找vector中3所在的位置的iteration
auto pos = find(v.begin(), v.end(), 3);
//在pos位置之前插入100
v.insert(pos, 100);
PrintVector(v);
// 删除pos位置的值
pos = find(v.begin(), v.end(), 3); // 迭代器已实现,需要重新赋值
v.erase(pos);
PrintVector(v);
}
// iteration失效问题
void TestVector4()
{
myvector::vector<int> v;
for (int i = 1; i < 5; ++i)
v.push_back(i);
//删除pos位置的数据,导致pos迭代器失效
auto pos = find(v.begin(), v.end(), 3);
v.erase(pos);
//cout << *pos << endl; // 此处会导致非法访问
PrintVector(v);
// 在pos位置插入数据,导致pos迭代器失效。
// insert会导致迭代器失效,是因为insert可
// 能会导致增容,增容后pos还指向原来的空间,而原来的空间已经释放了。
pos = find(v.begin(), v.end(), 2);
v.insert(pos, 30);
//cout << *pos << endl; // 此处会导致非法访问
PrintVector(v);
//实现删除v中的所有偶数
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0) {
it = v.erase(it);
}
else {
++it;
}
}
PrintVector(v);
}
class Solution {
public:
myvector::vector<myvector::vector<int>> generate(int numRows)
{
myvector::vector<myvector::vector<int>> vv;
vv.resize(numRows);
for (size_t i = 0; i < vv.size(); ++i)
{
// 每行开辟 i + 1个元素,并且初始化为0
vv[i].resize(i + 1, 0);
// 每行首尾元素得value为1
vv[i][0] = 1;
vv[i][vv[i].size() - 1] = 1;
}
for (size_t i = 0; i < vv.size(); ++i)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
// 如果有下标为0的元素,说明要到上一层对应的中间元素进行相加
if (vv[i][j] == 0)
// 这个元素的值通过规律可以发现:可再i-1行中第j和j-1的元素相加得到
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
return vv;
}
};
// 对杨辉三角进行测试,此处涉及更深的深浅拷贝问题(拷贝构造函数中扩容应使用深拷贝进行拷贝数据)
void Test_Solution()
{
Solution s;
myvector::vector<myvector::vector<int>> vv = s.generate(5);
for (size_t i = 0; i < vv.size(); ++i)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
cout << vv[i][j] << ' ';
}
cout << endl;
}
}
void reserve_iterator()
{
// 测试反向迭代器
cout << endl;
int Array[] = { 1, 2, 3, 4 };
myvector::vector<int> v(Array, Array + sizeof(Array) / sizeof(int));
for (auto rit = v.crbegin(); rit != v.crend(); ++rit)
{
cout << *rit << " ";
}
cout << endl;
}
int main()
{
TestVector1();
TestVector2();
TestVector3();
TestVector4();
Test_Solution();
reserve_iterator();
return 0;
}
全部内容已写完,感谢大家支持!!!