文章目录
- 比喻与理解
- 一、定义
- 二 、在使用层面
- 三、在实现层面
- 四、vector的成员变量
- 五、vector的迭代器
- 六、vector的容器函数
- 七、vector的其他函数
- 1. reserve更新容器空间大小:vector_name.reserve(size_type);
- 2. resize大小:vector_name.resize(size_type n);
- 3. vector的尾删函数: vector_name.pop_back();
- 4. vector的删除函数: vector_name.erase(size_type);
- 八、vector的默认构造
- 1. 默认构造函数:vector<T> vector_name();
- 2. 带有初始大小的构造函数:vector<T> vector_name(size_type n);
- 3. 带有初始值和大小的构造函数:vector<T> vector_name(size_type n, const T& value);
- 4. 带有迭代器范围的构造函数:vector<T> vector_name(InputIterator first,InputIterator last);
- 5. 拷贝构造函数:vector vector_name_1(vector_name_2);
- 6. 赋值运算符重载:vector_name_1=vector_name_2;
- 7. 析构函数: ~vector()
- 8. const取地址运算符重载: vector_name[size_type n]
- 9. find()寻找元素
比喻与理解
一、定义
vector是一个可以动态增长的数组,即变长数组。它依靠类和对象的特性实现。
vector比string设计的更合理,减少了很多冗余的库函数。
二 、在使用层面
vector的使用与string相似,初始化与数组类似;
无流插入流提取——vector使用范围for实现打印;
vector的接口与stl保持一致通用。
三、在实现层面
- vector是一个可以动态增长的数组,即变长数组。它依靠类和对象的特性实现;
- 由于vector的设计在string之后吸收了相关的经验,它的设计比string更加合理;
- vector实现时无需手动提供下标与扩容;
不同编译器内部各个功能的实现使用了很多复杂的指针关系,当我们学习理解时应当将其简化成原生指针。
四、vector的成员变量
我们最好在成员变量声明处给出缺省值。
private:
//各个成员函数的使用成员变量前全部统一初始化,防止不同编译器的其他初始化
iterator _start=nullptr; // 指向数据块的开始
iterator _finish = nullptr; // 指向有效数据的尾
iterator _endofstorage = nullptr; // 指向存储容量的尾
五、vector的迭代器
vector的结构允许我们使用原生指针去管理它,使用原生指针使其高效管理自身的内容。
// Vector的迭代器是一个原生指针
typedef T* iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
typedef const T* const_iterator; //迭代器指向的内容不能++
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
const const_iterator cbegin() const
{
return _start;
}
const const_iterator cend() const
{
return _finish;
}
六、vector的容器函数
我们使用的定位容器位置的函数,返回的是容器元素的内存位置和相对位置差值。
设置const版本,可以让普通对象和const对象都使用同一个函数定位;
capacity
//size_t size()
//{
// return _finish - _start;
//}
//size_t capacity()
//{
// return _endofstorage - _start;
//}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
七、vector的其他函数
1. reserve更新容器空间大小:vector_name.reserve(size_type);
适应性地扩大空间所有权,未赋予空间使用权;
void reserve(size_t n)
{
if(n>capacity())
{
T* tmp = new T[n];
size_t sz = size();
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * size()); //浅拷贝
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = sz + _start;
_endofstorage = _start + n;
}
}
我们经判断后认为需要扩容时,会通过以新的空间大小n为准new一个新的对象,再将旧空间的内容深拷贝到新的空间内,其内存地址和容器所有权大小更新,空间使用权大小会保持不变;
2. resize大小:vector_name.resize(size_type n);
刚性地扩大或缩小空间使用权、使用权;
// T int
// int类型没有构造函数的说法,但C++对int模板进行了升级,使其可以在与T()交接的情况下被认为有构造函数
// T string
// t vector<int>
//T()是匿名对象,用来接收各种数据类型
void resize(size_t n, const T& value = T())
{
if (n < size())
{
_finish = _start + n ;
}
else
{
reserve(n);
while (_finish < _start + n)
{
*_finish = value;
++_finish;
}
}
}
扩大或缩小对象的空间所有权大小果需要扩大空间则:
- 如果新的空间使用权size大小大于原容器空间所有权capacity大小,则需要同步扩大容器空间所有权capacity和空间使用权size,二者扩容到新地size为止(capacity可能会大于size),然后使用给定的value去填充size未利用空间,对象成员变量同步更新;
- 如果新的空间使用权size大小大于原对象空间size而小于capacity,则未利用的空间填充value
,需要先扩大容器,则会扩容直到可以存储我们的新对象大小,然后使用给定的value去填充未被使用的空间,其容器大小和空间使用权会更新;- 如果需要缩小
resize()、(resize内含reserve)代码演示如下:
void test3()
{
cout << "测试resize()" << endl;
vector<int*> v1;
v1.resize(10);
vector<string> v2;
v2.resize(10,"xxx");
cout << "v1" << endl;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
cout << "v2" << endl;
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
cout << endl;
}
3. vector的尾删函数: vector_name.pop_back();
void pop_back()
{
erase(_finish-1);
}
代码演示如下:
void test1()
{
cout << "试验push_back" << endl;
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(5);
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
cout << "试验迭代器" << endl;
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
cout << endl;
cout << "试验pop_back" << endl;
v.pop_back();
v.pop_back();
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
cout << endl;
}
4. vector的删除函数: vector_name.erase(size_type);
iterator erase(iterator pos)
{
assert(pos>=_start);
assert(pos<_finish);
iterator it = pos + 1;
while (it < _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
代码演示如下:
void test5()
{
cout << "测试erase()" << endl;
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
cout << "头删" << endl;
v.erase(v.begin());
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
cout << "中间删5" << endl;
v.erase(v.begin() + 3);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
cout << "尾删" << endl;
v.erase(v.end() -1);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
cout << endl;
}
八、vector的默认构造
默认构造函数、拷贝构造函数、赋值运算符重载、取地址及const取地址运算符重载、析构函数。
为了避免过耦合,下面我会把各个功能分开实现;
1. 默认构造函数:vector vector_name();
创建一个空的vector对象,其中T是vector中元素的类型。
//构造函数
vector()
//:_start(nullptr)
//, _finish(nullptr)
//, _endofstorage(nullptr)
{}
2. 带有初始大小的构造函数:vector vector_name(size_type n);
创建一个包含n个默认初始化元素的vector对象。
3. 带有初始值和大小的构造函数:vector vector_name(size_type n, const T& value);
创建一个包含n个值为value的元素的vector对象。
想要实现该功能必须先实现push_back()、reserve();
而想要实现push_back()就必须实习insert();
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start+len; //扩容后pos失效,在此更新
}
iterator end = _finish - 1;
while (end >= pos) //挪动数据为插入腾出地方
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
void push_back(const T& x)
{
insert(end(), x);
}
vector(int n, const T& value = T())
//:_start(nullptr)
//,_finish(nullptr)
//,_endofstorage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; i++)
{
push_back(value);
}
}
4. 带有迭代器范围的构造函数:vector vector_name(InputIterator first,InputIterator last);
创建一个包含[first, last)范围内元素的vector对象,其中InputIterator是迭代器类型.
template<class InputIterator>
vector(InputIterator first, InputIterator last)
//vector(size_t n, const T& value = T())面对v(int,int)时会出现混淆的问题
//所以上面n的类型点名写成int,单独写一个int重载版本
//:_start(nullptr)
//, _finish(nullptr)
//, _endofstorage(nullptr)
{
while (first != last)
{
push_back(*first);
++first;
}
}
1-3函数代码演示如下:
void test7()
{
cout << "测试各种初始化()" << endl;
vector<string> v1(10, "xxxx");
for (size_t i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
for (auto e:v1)
{
cout << e << " ";
}
cout << endl;
vector<int> v2(10, 1);
for (size_t i = 0; i < v1.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
cout << endl;
cout << endl;
}
5. 拷贝构造函数:vector vector_name_1(vector_name_2);
//拷贝构造C
vector(const vector<T>& v)
//:_start(nullptr)
//, _finish(nullptr)
//, _endofstorage(nullptr)
{
reserve(v.capacity());
for (auto& e : v)
{
push_back(e);
}
}
代码演示如下:
void test6()
{
cout << "测试拷贝构造()" << endl;
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vector<int> v2(v1);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
vector<int> v3;
v3 = v1;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
cout << endl;
}
6. 赋值运算符重载:vector_name_1=vector_name_2;
//想要实现赋值运算符重载,必须先实现swap()函数
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> tmp)
{
swap(tmp);
return *this;
};
7. 析构函数: ~vector()
//析构函数
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
8. const取地址运算符重载: vector_name[size_type n]
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
//前面的const:返回值不能改动;后面的const:指定const对象调用该函数
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
9. find()寻找元素
vector里面没有自己的find,在算法库里面有通用的find模板。
只有string的find是自己自带的,其专门针对字符与字符串的情况;
vector<int> vv(10,0);
cout<<find(vv.begin(),vv.end(),1);
代码演示如下:
void test8()
{
cout << "find()" << endl;
vector<int> vv(10,0);
cout<<find(vv.begin(),vv.end(),1);
}