目录
capacity,size,operator[],iterator,swap,operator=,~vector()析构函数:
注释:
(1)内置类型的默认构造:
int :0
double:0.0
char '\0'(char的默认值为'\0'而非0或者’ ‘因为\0对应的ascll码值为0)
string ""
1.vector的定义:
vector在实际运用当中通常是以如下方式:
vector<T> name;
其中T为模板参数,可以是自定义类型也可以是内置类型。
2.vector的初始化与赋值:
花括号初始化:
cpp继承了c的初始化方式可以利用花括号进行初始化:
vector<int> v{1,2,3,4,5,6};
上述例子中我们构造了一个容量为6数据类型为int的vector数组并将其数据初始化为1,2,3,4,5,6。
圆括号初始化:
由于在cpp中实现了利用圆括号的赋值方法所以我们可以利用如下方式:
vector<int> v(10); // 10个0
vector<int> v(10,1); // 10个1
这里我们利用圆括号实现了两种构造,第一种构造10个int初值为0的vector,第二种构造了10个初值为1的vector。
对应如下的两种构造:
赋值:
想要对任意位置的元素进行赋值操作我们只需要通过下标访问即可:
vector<int> v(5);
v[0] = 5;
for(int i = 0;i < v.szie();i++)
{
v[i] = i;
}
3.iterator迭代器:
vector拥有迭代器,其作用类似于指针。
vector的迭代器可以帮助我们访问vector元素。
存在迭代器之后我们的初始化方式又多了一种:
利用iterator初始化vector :
vector<int> v1(10);
vector<int> v2(v1.begin(),v1.end());
这种初始化方式就是利用一个已经存在的vector去构造一个新的vector同时将v1的数据会拷贝到v2当中,简单来说就是利用v1的数据去构造了一个v2。
4.模拟实现vector:
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
{}//无参默认构造
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;
};
首先我们实现了一个基础的vector模板,包括了iterator的定义,_endofstorage是表示该数组大小。
基础函数实现:
capacity,size,operator[],iterator,swap,operator=,~vector()析构函数:
size_t capacity() const
{
return _endofstorage - _start;
}
size_t size() const
{
return _finish - _start;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
reserve:
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldsize = size();
T* tem = new T[n];//new出来的空间已经初始化
if (_start)
{
memcpy(tem, _start, oldsize * sizeof(T));
delete[] _start;//释放start所指向的空间避免造成内存泄漏
}
_start = tem;
_finish = _start + oldsize;
_endofstorage = _start + n;
}
}
我们利用memcpy函数将旧的vector空间数据拷贝到新的tem当中去,此时并不是浅拷贝,因为我们申请了新的空间并且将数据转移到了tem当中。
push_back:
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2; //处理扩容,2倍扩
reserve(newcapacity);//复用reserve函数
}
*_finish = x;
++_finish;
}
pop_back:
void pop_back()
{
assert(size() > 0);
--_finish;
}
pop_back函数为尾删,因此我们只需要调整尾指针的位置即可,下一次插入数据时会将源数据覆盖。assert断言避免越界。
insert:
void insert(iterator pos, const T& x)
{
assert(pos <= _finish);
assert(pos >= _start);
if (_finish == _endofstorage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;// 扩容后更新迭代器
}
memmove(pos + 1, pos, sizeof(T) * (_finish - pos));
*pos = x;
++_finish;
}
两个assert语句可以合并但是分开写利于查询左越界还是右越界。
插入之前进行判满操作避免越界。
进行扩容操作之后要注意更新迭代器,否则会造成迭代器失效,后续会有详解。
resize:
void resize(size_t n,T val = T())//内置类型本身没有默认构造,但是cpp对内置类型进行了升级。
{
if (n > size())
{
reserve(n);
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
resize传参当中我们利用了T val = T();但是在c当中内置类型并没有默认构造,cpp为了统一利用模板对内置类型进行了升级,增加了默认构造(1)。
erase:
void erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator it = pos + 1;
while (it < _finish)
{
*(it - 1) = *it;
++it;
}
_finish--;
}
iterator迭代器失效:
迭代器失效的本质是由扩容引起的!
在reserve(内部)和insert(外部)过程中,由于异地扩容导致原迭代器所指向的地址已经被释放。
由于erase和insert都进行了对空间的修改,所以cpp STL库中对他们添加了返回值iterator
返回的迭代器:
返回的迭代器是一个指向新的经过了erase操作后的空间,以此来避免迭代器失效的产生(insert同理)。
5.reserve与insert的优化:
在上述实现中reserve和insert看似没有问题事实上当我们申请一个如下的vector时将会导致程序崩溃:
vector<string> vstr;
vstr.push_back("123455");
vstr.push_back("123455");
vstr.push_back("123455");
vstr.push_back("123455");
vstr.push_back("123455");
当我们进行上述操作时由于插入的数据超过了4个,所以需要进行扩容操作而string类型由于我们使用的是memcpy和memmove函数造成了string的浅拷贝导致了数据丢失,新的string指向的空间已经被释放。想要解决这个问题可以通过赋值的方式解决:
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldsize = size();
T* tem = new T[n];//new出来的空间已经初始化
if (_start)
{
//memcpy(tem, _start, oldsize * sizeof(T));
for (size_t i = 0; i < oldsize; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tem;
_finish = _start + oldsize;
_endofstorage = _start + n;
}
}
void insert(iterator pos, const T& x)
{
assert(pos <= _finish);
assert(pos >= _start);
if (_finish == _endofstorage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;// 扩容后更新迭代器
}
//浅拷贝
//memmove(pos + 1, pos, sizeof(T) * (_finish - pos));
//扩容会导致迭代器失效,需要记录pos到start的偏移量
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
-end;
}
*pos = x;
++_finish;
也可以通过引用计数的写时拷贝解决。
6.构造函数:
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
// //传统写法
//vector( const vector<T>& v)
//{
// //_start = new T[v.capacity()];
// //memcpy(_start,v._start, v.size() * sizeof(T));
// //_finish = _start + v.size();
// //_endofstorage = _start + v.capacity();
//}
//现代写法
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
reserve(v.capacity);
for (const auto& e : v)
{
push_back(e);
}
}
/*
指向连续的物理空间就是天然的迭代器
*/
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
vector(size_t n, const T& val = T())
{
resize(n,val);
}
//为了解决同为int类型匹配到vector(InputIterator first, InputIterator last)
vector(int n, const T& val = T())
{
resize(n, val);
}
7.完整代码:
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
namespace liu
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
{}
// //传统写法
//vector( const vector<T>& v)
//{
// //_start = new T[v.capacity()];
// //memcpy(_start,v._start, v.size() * sizeof(T));
// //_finish = _start + v.size();
// //_endofstorage = _start + v.capacity();
//}
vector(const vector<T>& v)
{
reserve(v.capacity);
for (const auto& e : v)
{
push_back(e);
}
}
/*
指向连续的物理空间就是天然的迭代器
*/
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
vector(size_t n, const T& val = T())
{
resize(n,val);
}
//为了解决同为int类型匹配到vector(InputIterator first, InputIterator last)
vector(int n, const T& val = T())
{
resize(n, val);
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldsize = size();
T* tem = new T[n];//new出来的空间已经初始化
if (_start)
{
//memcpy(tem, _start, oldsize * sizeof(T));
for (size_t i = 0; i < oldsize; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tem;
_finish = _start + oldsize;
_endofstorage = _start + n;
}
}
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
*_finish = x;
++_finish;
}
void pop_back()
{
assert(size() > 0);
--_finish;
}
/*
insert:
与erase类似,返回值为iterator,解决迭代器失效;
*/
void insert(iterator pos, const T& x)
{
assert(pos <= _finish);
assert(pos >= _start);
if (_finish == _endofstorage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;// 扩容后更新迭代器
}
//浅拷贝
//memmove(pos + 1, pos, sizeof(T) * (_finish - pos));//扩容会导致迭代器失效,需要记录pos到start的偏移量
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
-end;
}
*pos = x;
++_finish;
}
//int i = 0;
//int j = int();
//int K = int(10);
/*
resize:
大小在capacity与size之间
大于size
小于capacity
*/
void resize(size_t n,T val = T())//内置类型本身没有默认构造,但是cpp对内置类型进行了升级。
{
if (n > size())
{
reserve(n);
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
/*
erase:
std库中返回值为iterator,来解决迭代器失效的问题;
*/
void erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator it = pos + 1;
while (it < _finish)
{
*(it - 1) = *it;
++it;
}
_finish--;
}
size_t capacity() const
{
return _endofstorage - _start;
}
size_t size() const
{
return _finish - _start;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;
};
}