一、简单认识vector
1.vector是一种有序顺序表模板;2.vector插入或者删除都存在迭代器失效问题;
与string的区别:
1.相较于string的设计简单了不少,且vector是一个模板;2.vector<char>并不能替代string,因为string有’\0’结尾,自动实现,而vector可以’\0’结尾,但不能自动实现;3.string的接口更加丰富,如:+=,比较大小的设计更有意义,find等字符函数的用途;
template < class T, class Alloc = allocator<T> > class vector; // generic template
stl的所有容器都使用了空间配置器(内存池),
二、vector的使用
1.成员类型
2.默认成员函数
2.1构造函数
//1.无参
explicit vector (const allocator_type& alloc = allocator_type());
//2.n个T类型的值
explicit vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
//3.用迭代器模板初始化,函数模板隐式实例化。可以使用自己类型的迭代器,也可以使用其他类型的。
//因为指针是天然的迭代器,所以也支持。
template <class InputIterator>
vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
3.迭代器
与string一样。
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
4.堆空间操作
//1
size_type size() const;
//2
void resize (size_type n, value_type val = value_type());
//3
void reserve (size_type n);
void test()
{
vector<int> v;
//v.reserve(10 * sizeof(int));//使用reserve主要是一次性开部分空间,为了减少扩容消耗。
v.resize(10);//这样就size不为零。
for (size_t i = 0; i < 10; ++i)
{
v[i] = i;//operato[]r会使用断言,检查pos位置是否小于size。size为0,一定报错。at是抛异常。
}
}
5.元素访问
//1
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;
//2
reference at (size_type n);
const_reference at (size_type n) const;
//3
reference front();
const_reference front() const;
//4
reference back();
const_reference back() const;
//5
value_type* data() noexcept;
const value_type* data() const noexcept;
6.修改数据
//1.尾插
void push_back (const value_type& val);
//2.尾删
void pop_back();
//3.任意位置插入
iterator insert (iterator position, const value_type& val);//迭代器位置往后一个值
void insert (iterator position, size_type n, const value_type& val);//迭代器往后n个值
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
//4.任意位置删除
//设计迭代器失效的问题
iterator erase (iterator position);//删除某个位置
iterator erase (iterator first, iterator last);//删除某段区间
//5.swap
void swap (vector& x);
//6.清理数据
void clear()
//7.重新赋值
template <class InputIterator>
void assign (InputIterator first, InputIterator last);
void assign (size_type n, const value_type& val);
三、算法algorithm
使用迭代器实现不同容器的复用。
#include<algorithm>
//1.sort
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
//2.swap
template <class T> void swap (T& a, T& b);
//3.find
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
An iterator to the first element in the range that compares equal to val.
If no elements match, the function returns last.
补充:1.对于指针变量来说,指针变量存放的是地址,使用解引用底层会到存放的地址处进行访问,对于空指针和野指针,进行访问就会导致程序崩溃。2.一级指针变量的地址被设置成了只能存放在二级指针变量的空间。3.对于获得使用权的空间,使用的是不同的类型来划定空间的范围来进行访问,如:int*的指针变量,每次可访问4个字节,对它的++就被识别成了按照4个字节进行移动。
四、vector的模拟实现
补充:
1.c++初始化列表出现以后对内置类型进行了升级,支持了默认构造;2.注意扩容逻辑对于自定义类型不能使用浅拷贝memcpy,可以使用赋值运算符重载;3.插入删除时会存在迭代器失效问题,所以没吃都要重置迭代器;4.对于拷贝构造和赋值重载可以复用resize和reserve,使用前必须在初始化列表进行初始化,底层会实现对成员变量的修改,要注意的是reserve后要clear数据,然后必须用尾插,resize直接使用赋值,或者clear之后尾插;5.reserve还有finish_没有初始化,resize全部初始化了;6.使用迭代器区间构造,类模板里面定义函数模板。
1.成员变量
public:
typedef T value_type;
typedef value_type* iterator;
protected:
iterator start;
iterator finish;
iterator end_of_storage;
//并不是以下的,所以需要了解一下构造函数和插入接口或者扩容逻辑
iterator a_;
size_t size_;
size_t capacity;
2.默认成员函数
//1.默认构造
//1.1
vector() : start_(nullptr), finish_(nullptr), end_of_storage_(nullptr) {}
//1.2
vector(size_t n, const T &val = T()) : start_(nullptr), finish_(nullptr), end_of_storage_(nullptr)
{
resize(n, val);
}
//1.3
template <class InputIteartor>//可能会和1.2冲突,vector<int>v(10u,5),才会识别成1.2
vector(InputIteartor first, InputIteartor last): start_(nullptr), finish_(nullptr), end_of_storage_(nullptr)
{
while (first != last)
{
push_back(*first);
first++;
}
}
//2.拷贝构造
//2.1传统方式
vector(const vector<T> &v) : start_(nullptr), finish_(nullptr), end_of_storage_(nullptr)
{
start_ = new T[v.capacity()];
for (size_t i = 0; i < v.size(); i++)
{
start_[i] = v.start_[i];
}
finish_ = start_ + v.size();
end_of_storage_ = start_ + v.capacity();
}
//2.2复用reserve,resize
vector(const vector<T> &v) : start_(nullptr), finish_(nullptr), end_of_storage_(nullptr)
{
// reserve(v.capacity());
// for (auto e : v)
// {
// push_back(e);
// }
resize(v.size());
for (size_t i = 0; i < v.size(); i++)
{
start_[i] = v.start_[i];
}
}
//2.3现代方式
//3赋值重载
//3.1复用resize reserve
vector<T> &operator=(const vector<T> &v)
{
resize(v.size());//成员变量全部初始化好
for (size_t i = 0; i < v.size(); i++)
{
start_[i] = v.start_[i];//转换为调用自定义类型的赋值运算符重载
}
return *this;
reserve(v.capacity());//finish_没初始化
clear();
for (auto e : v)
{
push_back(e);//会初始化finish_
}
return *this;
}
//3.2现代方式
vector<T> &operator=(vector<T> v)
{
std::swap(start_, v.start_);
std::swap(finish_, v.finish_);
std::swap(end_of_storage_, v.end_of_storage_);
return *this;
}
//3.3传统方式
vector<T> &operator=(const vector<T> &v)
{
reserve(v.capacity());
clear();
//memcpy(start_, v.start_, sizeof(T) * v.size());//对于自定义类型memcpy会浅拷贝,会产生野指针问题,和内存泄漏。
for (auto e:v)
{
push_back(e);
}
return *this;
}
~vector()
{
if (start_)
{
delete[] start_;
start_ = finish_ = end_of_storage_ = nullptr;
}
}
3.插入接口
void push_back(const T &val)
{
// if (finish_ == end_of_storage_)
// {
// size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
// reserve(newcapacity);
// }
// *finish_ = val;
// ++finish_;
insert(end(), val);
}
// 注意迭代器失效的问题:1.野指针失效,内外迭代器都会因为异地扩容释放旧空间,导致pos失效了。
iterator insert(iterator pos, const T &val)
{
assert(pos >= start_ && pos <= finish_);
if (finish_ == end_of_storage_)
{
size_t len = pos - start_;
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
pos = start_ + len;
}
iterator end = finish_ - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = val;
finish_++;
return pos;
}
4.删除接口
//迭代器失效,最后一个位置的数据一般都不会挪动,再次访问最后一个位置就会出现问题。
//vs对迭代器进行了强制检查,访问程序直接就崩了;g++没有强制检查。
//为具有平台移植性,对于失效的迭代器不能访问。
iterator erase(iterator pos)
{
assert(pos >= start_ && pos < finish_);
iterator it = pos + 1;
while (it < finish_)
{
*(it - 1) = *it;
++it;
}
--finish_;
return pos;
}
void pop_back()
{
erase(end() - 1);
}
5.对于空间的操作
size_t capacity() const
{
return end_of_storage_ - start_;
}
size_t size() const
{
return finish_ - start_;
}
//对于自定义类型要注意memcpy会浅拷贝,如:vector<string>在扩容时使用了memcpy,
//对sting对象进行了浅拷贝
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldsize = size();
iterator tmp = new T[n];
if (start_)
{
// memcpy(tmp, start_, oldsize * sizeof(T));//扩容可能浅拷贝
for (size_t i = 0; i < oldsize; i++)
{
tmp[i] = start_[i];
}
delete[] start_;
}
// 问题
// start_ = tmp;
// finish_ = start_ + size(); // 调用size时,函数内的start_已经变了,不是0。
// end_of_storage_ = start_ + n;
// 解决1
// finish_ = tmp + size();
// start_ = tmp;
// end_of_storage_ = start_ + n;
// 解决2
start_ = tmp;
finish_ = start_ + oldsize;
end_of_storage_ = start_ + n;
}
}
bool empty()
{
return size() == 0;
}
void resize(size_t n, const T &x = T())
{
if (n < size())
{
finish_ = start_ + n;
}
else
{
reserve(n);
while (finish_ != start_ + n)
{
*finish_ = x;
++finish_;
}
}
}
void clear()
{
resize(0);
}
6.元素访问
T &operator[](size_t pos)
{
assert(pos < size());
return start_[pos];
}
const T &operator[](size_t pos) const
{
assert(pos < size());
return start_[pos];
}
7.迭代器
iterator begin()
{
return start_;
}
iterator end()
{
return finish_;
}
const_iterator begin() const
{
return start_;
}
const_iterator end() const
{
return finish_;
}
8.成员类型
typedef T *iterator;
typedef const T *const_iterator;