一、vector接口总览
namespace bit
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
iterator begin();
iterator end();
const_iterator cbegin();
const_iterator cend() const;
// construct and destroy
vector();
vector(int n, const T& value = T());
template<class InputIterator>
vector(InputIterator first, InputIterator last);
vector(const vector<T>& v);
vector<T>& operator= (vector<T> v);
~vector();
// capacity
size_t size() const ;
size_t capacity() const;
void reserve(size_t n);
void resize(size_t n, const T& value = T());
///access///
T& operator[](size_t pos);
const T& operator[](size_t pos)const;
///modify/
void push_back(const T& x);
void pop_back();
void swap(vector<T>& v);
iterator insert(iterator pos, const T& x);
iterator erase(Iterator pos);
private:
iterator _start; // 指向数据块的开始
iterator _finish; // 指向有效数据的尾
iterator _endOfStorage; // 指向存储容量的尾
};
}
为了防止与标准库当中的vector区分,我们将自己模拟实现的vector放在命名空间bit中。
二、模拟实现vector
2.1 默认成员函数的模拟实现
2.1.1 构造函数1
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
这是无参的构造函数,我们实现这个这个函数只需将三个成员变量设置成空指针即可。但C++支持成员变量设置缺省值,所以我们有更简洁的写法。
vector()
{}
private:
iterator _start = nullptr; // 指向数据块的开始
iterator _finish = nullptr; // 指向有效数据的尾
iterator _endOfStorage = nullptr; // 指向存储容量的尾
给成员变量一个缺省值空指针即可。
2.1.2 构造函数2
vector(int n, const T& value = T())
{
reserve(n);
for (size_t i = 0; i < n; i++)
{
push_back(value);
}
}
这个构造函数可以使用n个value来初始化vector,我们先用reserve开好n个空间,最后将value依次尾插到开好的空间即可。
2.1.3 构造函数3
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
这是用一段迭代器区间来对vector进行初始化,迭代器区间是其他容器的迭代器区间,对于其他容器的类型是不确定的,所以我们在这里要设计成函数模板,最后将每个数据依次尾插到容器中即可。
2.1.4 拷贝构造函数
vector(vector<T>& v)
{
reserve(v.size());
for (auto& x : v)
{
push_back(x);
}
}
拷贝构造函数的实现也十分简单,首先先开好空间,再用范围for遍历容器,最后将数据依次尾插到新容器中即可。
这里选需要注意深浅拷贝问题,如果使用memcpy函数进行拷贝,那么我们如果释放了旧空间,我们将找不到数据所在的空间了,因为memcpy是浅拷贝,两个容器实际上是共用一段空间,如果释放了旧空间,那么拷贝过后的容器也找不到数据,空间以及被释放掉了。
2.1.5 赋值重载函数
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;
}
赋值重载函数只需调用一个swap函数即可实现,而swap函数则是调用了标准库当中的swap函数,我们交换两个容器的值,那么左值与右值就交换了,便完成了赋值。
这里一样要注意深浅拷贝问题,一样不能使用memcpy进行拷贝,这种写法就是深拷贝。
2.1.6 析构函数
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endOfStorage = nullptr;
}
}
析构函数也十分简单,我们首先先判断容器是否为空,如果不为空就将所开的空间释放掉,再将三个指针都置为空指针。
2.2 迭代器相关函数的实现
typedef T* iterator;
typedef const T* const_iterator;
vector的迭代器实际上就是一个原生指针,是当前数据类型的指针。
2.2.1 begin和end的模拟实现
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
begin返回容器的首地址,end返回容器当中有效数据的下一个位置。
2.3 容量和大小函数的模拟实现
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endOfStorage - _start;
}
size返回的是当前容器的数据个数,capacity返回的是当前容器的容量,我们用_finish-_start就可以得出数据个数,同样的用_endOfStorage - _start就可以得出当前容器的容量。
2.4 reserve函数的模拟实现
template<class T>
void bit::vector<T>::reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size();
T* temp = new T[n];
for (size_t i = 0; i < old_size; i++)
{
temp[i] = _start[i];
}
delete[] _start;
_start = temp;
_finish = _start + old_size;
_endOfStorage = _start + n;
}
}
这里要注意reserve的扩容规则,如果n<capacity那么就不扩容,只有n>capacity的时候才进行扩容。 在扩容的时候我们要先将原先容器的长度记录下来,
在扩容完后,_start以及指向temp了,但此时的_finish还是指向原来容器的末尾,此时也要对_finish进行更新,而_start+old_size就是新容器_finish所指向的位置,所以在释放空间前要先记录原容器的长度,这样才能找到新容器中_finish所指向的准确位置,不对_finish进行更新,_finish-_size就会得出随机值,所得出的有效数据的个数就是错误的。
在这里一样要注意深浅拷贝的问题,我们一样不能够使用memcpy进行拷贝,否则释放旧空间时就找不到数据了。
2.5 resize函数的模拟实现
template<class T>
void bit::vector<T>::resize(size_t n, const T& value)
{
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish < _start + n)
{
*_finish = value;
++_finish;
}
}
}
这里也要注意resize的规则,如果n<size则是进行删除,最终容器中只剩下n个数据,当n>size时就是进行尾插,尾插_start+n-_finish个数据。
void resize(size_t n, const T& value = T());
这是resize函数的声明, value给的缺省值T()主要是为了应对string未初始化的情况,当然C++的内置类型也有自己的默认构造函数,所以内置类型未初始化也可以应用这个缺省值。
2.6 empty函数的模拟实现
bool empty()const
{
return _start == _finish;
}
empty可以判断该容器是否为空,我们只需判断_start和_finish两个指针的位置即可判断容器是否为空。
2.7 push_back函数的模拟实现
void push_back(const T& x)
{
if (_finish == _endOfStorage)
{
reserve(capacity() == 0 ? 4 : 2 * capacity());
}
*_finish = x;
++_finish;
}
首先判断容器的容量是否满了,满了需要扩容,没满就将数据尾插到_finish位置即可,最后让_finish++。
2.8 pop_back函数的模拟实现
void pop_back()
{
assert(!empty());
--_finish;
}
首先要判断容器是否为空,如果容器为空则不能进行尾删,不为空--_finish即可。
2.9 insert函数的模拟实现
template<class T>
T* bit::vector<T>::insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos < _finish);
if (_finish == _endOfStorage)
{
size_t lenth = pos - _start;
reserve(capacity() == 0 ? 4 : 2 * capacity());
pos = _start + lenth;
}
iterator end = _finish + 1;
while (pos <= end)
{
*end = *(end - 1);
--end;
}
*pos = x;
++_finish;
return pos;
}
首先判断位置是否合法,再判断是否需要扩容,在扩容时需要先记录原先容器pos和_start的相对位置,这样才能在扩容后找到要插入的位置,找到要插入的位置后,将pos及pos之后的位置全部往后移一位,最后在pos位置插入数据即可。
这里要注意的问题是,当你插入了一个数据并且还想使用pos位置的指针时,会达不到你想要的结果,这种情况是迭代器失效的问题,如果要想正确的使用pos位置的指针,就必须要对pos指针进行修改。
2.10 erase函数的模拟实现
template<class T>
T* bit::vector<T>::erase(iterator pos)
{
assert(pos < _finish);
assert(pos >= _start);
assert(!empty());
iterator it = pos + 1;
while (it != end())
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
erase可以删除pos位置的值,先判断位置是否合法以及容器是否为空,位置合法及容器不为空,就将pos位置之后的每一个数据依次往前挪一位,这样挪完位置之后pos位置的值便被覆盖掉了,完成删除。
2.11 operator[]函数的模拟实现
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
这是实现通过下标访问容器中的数据,我们只需检查下pos是否合法,合法返回pos所指向位置的值即可。
三、模拟实现vector完整代码
vector.h文件:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace bit
{
template<class T>
class vector
{
public:
// Vector的迭代器是一个原生指针
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator cbegin()
{
return _finish;
}
const_iterator cend() const
{
return _start;
}
// construct and destroy
vector()
{}
vector(int n, const T& value = T())
{
reserve(n);
for (size_t i = 0; i < n; i++)
{
push_back(value);
}
}
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
vector(vector<T>& v)
{
reserve(v.size());
for (auto& x : v)
{
push_back(x);
}
}
vector<T>& operator= (vector<T> v)
{
swap(v);
return *this;
}
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endOfStorage = nullptr;
}
}
// capacity
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endOfStorage - _start;
}
void reserve(size_t n);
void resize(size_t n, const T& value = T());
///access///
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const
{
assert(pos < size());
return _start[pos];
}
///modify/
void push_back(const T& x)
{
if (_finish == _endOfStorage)
{
reserve(capacity() == 0 ? 4 : 2 * capacity());
}
*_finish = x;
++_finish;
}
bool empty()const
{
return _start == _finish;
}
void pop_back()
{
assert(!empty());
--_finish;
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endOfStorage, v._endOfStorage);
}
iterator insert(iterator pos, const T& x);
iterator erase(iterator pos);
private:
iterator _start = nullptr; // 指向数据块的开始
iterator _finish = nullptr; // 指向有效数据的尾
iterator _endOfStorage = nullptr; // 指向存储容量的尾
};
template<class T>
void bit::vector<T>::reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size();
T* temp = new T[n];
for (size_t i = 0; i < old_size; i++)
{
temp[i] = _start[i];
}
delete[] _start;
_start = temp;
_finish = _start + old_size;
_endOfStorage = _start + n;
}
}
template<class T>
void bit::vector<T>::resize(size_t n, const T& value)
{
if (n < size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish < _start + n)
{
*_finish = value;
++_finish;
}
}
}
template<class T>
T* bit::vector<T>::insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos < _finish);
if (_finish == _endOfStorage)
{
size_t lenth = pos - _start;
reserve(capacity() == 0 ? 4 : 2 * capacity());
pos = _start + lenth;
}
iterator end = _finish + 1;
while (pos <= end)
{
*end = *(end - 1);
--end;
}
*pos = x;
++_finish;
return pos;
}
template<class T>
T* bit::vector<T>::erase(iterator pos)
{
assert(pos < _finish);
assert(pos >= _start);
assert(!empty());
iterator it = pos + 1;
while (it != end())
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
template<class container>
void print_container(container& v)
{
for (auto x : v)
{
cout << x << " ";
}
cout << endl;
}
void test_push_back()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
print_container(v);
}
void test_pop_back()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
print_container(v);
v.pop_back();
print_container(v);
}
void test_operator()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
print_container(v1);
vector<int> v2 = v1;
print_container(v2);
vector<int> v3;
v3.push_back(10);
v3.push_back(20);
v3.push_back(30);
v1 = v3;
print_container(v1);
print_container(v3);
v3[2] = 40;
print_container(v3);
}
void test_insert()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
print_container(v);
int x;
cin >> x;
auto p = find(v.begin(), v.end(), x);
if (p != v.end())
{
v.insert(p, 20);
}
print_container(v);
}
void test_erase()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
print_container(v);
int x;
cin >> x;
auto p = find(v.begin(), v.end(), x);
if (p != v.end())
{
v.erase(p);
}
print_container(v);
}
}
test.cpp文件:
#include"vector.h"
int main()
{
//bit::test_push_back();
//bit::test_pop_back();
//bit::test_operator();
//bit::test_insert();
bit::test_erase();
return 0;
}
四、 总结
以上便是模拟实现vector函数接口的过程,希望以上所讲能够对大家有所帮助,有帮助的话,别忘了一键三连哦,感谢各位。