🐱作者:傻响
🐱专栏:《C/C++ - STL》
🔥格言:你只管努力,剩下的交给时间!
目录
No.1.vector的介绍及使用
1.1 vector的介绍
1.2 vector的接口
vector的构造函数接口
vector的析构函数接口
vector的operator函数接口
vector的迭代器函数接口
vector 查看容量的函数接口
vector 元素访问函数接口
vector 的迭代器函数接口
vector的插入函数接口
No.2 vector接口的使用
2.1 在末尾添加元素
2.2 插入数据
2.3 删除数据
2.4 请求改变容量
2.5 元素访问
No.3 vector接口的模拟实现
No.1 实现测试一:
3.1.1 构造函数的模拟实现
No.2 实现测试二:
3.2.1 尾插模拟实现
3.2.2 扩容实现
3.2.3 调整数据大小
No.3 实现测试三:
3.3.1 打印函数的实现
3.3.2 迭代器
3.3.3 operator[]取位置数据
No.4 实现测试四:
3.4.1 指定位置插入数据函数实现
3.4.2 指定位置删除数据函数实现
No.5 赋值函数的实现:
3.5.1 operator=赋值实现
3.5.2 拷贝构造实现
No.6 其他函数实现:
3.6.1 判断容器是否为空
练习题验证
No.1.vector的介绍及使用
1.1 vector的介绍
-
vector是表示可变大小数组的序列容器。
-
就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。
-
本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大 小。
-
vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是 对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
-
因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。
-
与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list 统一的迭代器和引用更好。 使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习
-
详细的STL文档请参考:https://cplusplus.com/reference/vector/vector/
1.2 vector的接口
vector的构造函数接口
函数接口 | 函数说明 |
---|---|
explicit vector (const allocator_type& alloc = allocator_type()); | 构造函数1,构造一个向量,根据使用的构造函数版本初始化其内容: |
explicit vector (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type()); | 构造函数2,构造一个向量,根据使用的构造函数版本初始化其内容: |
template <class InputIterator> vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()); | 构造函数3,构造一个向量,根据使用的构造函数版本初始化其内容: |
vector (const vector& x); | 拷贝构造 |
vector的析构函数接口
函数接口 | 函数说明 |
---|---|
~vector(); | 销毁容器对象。 |
vector的operator函数接口
函数接口 | 函数说明 |
---|---|
vector& operator= (const vector& x); | 将新内容分配给容器,替换其当前内容,并相应地修改其大小。 |
vector的迭代器函数接口
函数接口 | 函数说明 |
---|---|
iterator begin();const_iterator begin() const; | 返回指向vector中第一个元素的迭代器。 |
iterator end();const_iterator end() const; | 返回指向vector容器中倒数元素的迭代器。 |
reverse_iterator rbegin(); const_reverse_iterator rbegin() const; | 返回指向vector中最后一个元素的反向迭代器(反向迭代器) |
reverse_iterator rend(); const_reverse_iterator rend() const; | 返回一个反向迭代器,指向vector中第一个元素之前的理论元素(反向迭代器) |
const_iterator cbegin() const noexcept; | 返回指向容器中第一个元素的 |
const_iterator cend() const noexcept; | 返回一个const_iterator对象,指向容器的末尾元素。 |
const_reverse_iterator crbegin() const noexcept; | 返回const_reverse_iterator来反转开头 ,返回指向容器中最后一个元素的(反向迭代器) |
const_reverse_iterator crend() const noexcept; | 返回一个const_reverse_iterator,指向容器中第一个元素之前的理论元素 |
vector 查看容量的函数接口
函数接口 | 函数说明 |
---|---|
size_type size() const; | 返回大小 |
size_type max_size() const; | 返回最大大小 |
bool empty() const; | 测试vector是否为空 |
void resize (size_type n, value_type val = value_type()); | 改变大小 |
void reserve (size_type n); | 请求改变容量 |
size_type capacity() const; | 返回已分配存储的大小 |
void shrink_to_fit(); | 缩小到合适的空间 |
vector 元素访问函数接口
函数接口 | 函数说明 |
---|---|
reference operator[] (size_type n); const_reference operator[] (size_type n) const; | 访问元素 |
reference at (size_type n); const_reference at (size_type n) const; | 访问元素 |
reference front(); const_reference front() const; | 访问第一个元素 |
reference back(); const_reference back() const; | 访问最后一个元素 |
value_type* data() noexcept;const value_type* data() const noexcept; | 访问数据:返回指向vector容器内部用于存储其拥有元素的内存数组的直接指针。 |
vector 的迭代器函数接口
函数接口 | 函数说明 |
---|---|
begin + end(重点) | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin + rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator |
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; |
vector的插入函数接口
函数接口 | 函数说明 |
---|---|
void push_back (const value_type& val); | 在末尾添加元素 |
void pop_back(); | 在末尾删除元素 |
template <class InputIterator> void assign (InputIterator first, InputIterator last); void assign (InputIterator first, InputIterator last); | 修改vector的内容 |
iterator insert (iterator position, const value_type& val); void insert (iterator position, size_type n, const value_type& val); template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last); | 插入pos位置数据 |
iterator erase (iterator position); iterator erase (iterator position); | 删除pos位置数据 |
void swap (vector& x); | 交换 |
void clear(); | 清除容器 |
template <class... Args>iterator emplace (const_iterator position, Args&&... args); | 构造和插入元素 |
template <class... Args> void emplace_back (Args&&... args); | 构造并在末尾插入元素 |
No.2 vector接口的使用
我们之前做过C语言的顺序表,现在我们使用一下C++的顺序表,先来看第一个函数
2.1 在末尾添加元素
void push_back (const value_type& val); 在末尾添加元素
描述:
在向量的最后一个元素之后添加一个新元素。val的内容被复制(或移动)到新元素中。容器大小增加1,当且仅当新的向量大小超过当前向量容量时,将自动重新分配已分配的存储空间(扩容)。
// 定义Vector.
vector<int> iVector;
// 插入数据
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
这里我们想一下Vector的打印方式是如何打印的呢?是不是和String一样呢?
// Vector的打印.
// No.1: 暴力打印.
for (size_t i = 0; i < iVector.size(); i++)
{
cout << iVector[i] << " ";
}
cout << endl;
// No.2:范围for打印.
for (auto v : iVector)
{
cout << v << " ";
}
cout << endl;
// No.3: 使用迭代器.
// begin()返回初始迭代器
// end() 返回迭代器结束
vector<int>::iterator itBegin = iVector.begin();
while (itBegin < iVector.end())
{
cout << *itBegin << " ";
++itBegin;
}
cout << endl;
// No.4: 使用反向迭代器.
vector<int>::reverse_iterator ritBegin = iVector.rbegin();
while (ritBegin < iVector.rend())
{
cout << *ritBegin << " ";
++ritBegin;
}
cout << endl;
2.2 插入数据
vector不提供头插,可以直接用insert进行插入数据。
// Vector的打印.
// No.1: 暴力打印.
for (size_t i = 0; i < iVector.size(); i++)
{
cout << iVector[i] << " ";
}
cout << endl;
// No.2:范围for打印.
for (auto v : iVector)
{
cout << v << " ";
}
cout << endl;
// No.3: 使用迭代器.
// begin()返回初始迭代器
// end() 返回迭代器结束
vector<int>::iterator itBegin = iVector.begin();
while (itBegin < iVector.end())
{
cout << *itBegin << " ";
++itBegin;
}
cout << endl;
// No.4: 使用反向迭代器.
vector<int>::reverse_iterator ritBegin = iVector.rbegin();
while (ritBegin < iVector.rend())
{
cout << *ritBegin << " ";
++ritBegin;
}
cout << endl;
2.3 删除数据
删除数据和插入数据一样,同样没有头删,可以利用迭代器。
删除数据,也可以指定删除一段区间。
vector<int> iVector;
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
for (size_t i = 0; i < iVector.size(); i++)
{
cout << iVector[i] << " "; // 1、2、3、4、5
}
cout << endl;
iVector.insert(iVector.begin(), 100);
for (auto v : iVector)
{
cout << v << " "; // 100、1、2、3、4、5
}
cout << endl;
vector<int>::iterator findPos = find(iVector.begin(), iVector.end(), 1);
iVector.insert(findPos, 200);
for (auto v : iVector)
{
cout << v << " "; // 100、200、1、2、3、4、5
}
cout << endl;
// 尾删.....
iVector.pop_back();
for (auto v : iVector)
{
cout << v << " "; // 100、200、1、2、3、4
}
cout << endl;
findPos = find(iVector.begin(), iVector.end(), 200);
iVector.erase(findPos);
for (auto v : iVector)
{
cout << v << " "; // 100、1、2、3、4
}
cout << endl;
// 删指定位置的数据...
iVector.erase(++iVector.begin(), --iVector.end());
for (auto v : iVector)
{
cout << v << " "; // 100、4
}
cout << endl;
2.4 请求改变容量
void reserve (size_type n); .// 请求改变容量
描述:
要求向量容量至少足以包含n个元素。如果n大于当前的向量容量,函数将使容器重新分配其存储空间,将其容量增加到n(或更大)。在所有其他情况下,函数调用不会导致重新分配,向量容量也不受影响。这个函数对向量的大小没有影响,也不能改变它的元素
void resize (size_type n, value_type val = value_type()); // 改变大小
描述:
调整容器的大小,使其包含n个元素。如果n小于当前容器大小,则内容将减少到其前n个元素,删除超出的元素(并销毁它们)。如果n大于当前容器的大小,则内容将被扩展,在末尾插入所需的元素,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将对它们进行值初始化。如果n也大于当前容器容量,则自动重新分配已分配的存储空间。注意,此函数通过插入或删除容器中的元素来更改容器的实际内容。
// 定义Vector.
vector<int> iVector;
// 插入数据
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
cout << iVector.capacity() << endl;
// 扩容 -> 扩容
iVector.reserve(100);
cout << iVector.capacity() << endl;
// 缩容 -> 不会缩容
iVector.reserve(5);
cout << iVector.capacity() << endl;
// 使用resize()扩容缩容
iVector.resize(8); // 1、2、3、4、5、0、0、0
cout << iVector.capacity() << endl;
iVector.resize(15, 1); // 1、2、3、4、5、0、0、0、1、1、1、1、1、1...
cout << iVector.capacity() << endl;
iVector.resize(3); // 1、2、3
cout << iVector.capacity() << endl;
Vs下的扩容机制
// 测试vector的默认扩容机制
void TestVectorExpand()
{
vector<int> iVector;
size_t capacitySize = iVector.capacity();
cout << "making iVector grow:\n";
for (size_t i = 0; i < 100; ++i)
{
iVector.push_back(1);
if (capacitySize != iVector.capacity())
{
cout << "capacity changed: " << capacitySize << endl;
capacitySize = iVector.capacity();
}
}
}
/* making iVector grow:
capacity changed: 0
capacity changed: 1
capacity changed: 2
capacity changed: 3
capacity changed: 4
capacity changed: 6
capacity changed: 9
capacity changed: 13
capacity changed: 19
capacity changed: 28
capacity changed: 42
capacity changed: 63
capacity changed: 94 */ // 1.5倍
Linux下的扩容机制
// 测试vector的默认扩容机制
void TestVectorExpand()
{
vector<int> iVector;
size_t capacitySize = iVector.capacity();
cout << "making iVector grow:\n";
for (size_t i = 0; i < 100; ++i)
{
iVector.push_back(1);
if (capacitySize != iVector.capacity())
{
cout << "capacity changed: " << capacitySize << endl;
capacitySize = iVector.capacity();
}
}
}
/* making iVector grow:
capacity changed: 0
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64 */ // 2倍
2.5 元素访问
operator[]与at的区别。
vector<int> iVector;
iVector.reserve(10);
for (size_t i = 0; i < 10; i++)
{
cout << iVector[i] << endl; // 断言报错。
}
for (size_t i = 0; i < 10; i++)
{
cout << iVector.at(i) << endl; // 抛出异常。
}
assign()。
template <class InputIterator>
void assign (InputIterator first, InputIterator last);
void assign (InputIterator first, InputIterator last);
指定向量内容,将新内容赋给向量,替换其当前内容,并相应地修改其大小。
调用之前容器中保存的任何元素都将被销毁并替换为新构造的元素(不发生元素赋值)。 当且仅当新的向量大小超过当前向量容量时,这将自动重新分配已分配的存储空间
vector<int> iVector;
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
for (auto v : iVector)
{
cout << v << endl; // 1、2、3、4、5
}
iVector.assign(10, 20);
for (auto v : iVector)
{
cout << v << endl; // 20、20、20、20、20.....
}
assign也可以使用迭代器进行操作。
vector<int> iVector;
iVector.push_back(1);
iVector.push_back(2);
iVector.push_back(3);
iVector.push_back(4);
iVector.push_back(5);
for (auto v : iVector)
{
cout << v << " "; // 1、2、3、4、5
}
cout << endl;
vector<int> iVectorT;
iVectorT.assign(iVector.begin(), iVector.end());
for (auto v : iVector)
{
cout << v << " "; // 1、2、3、4、5
}
cout << endl;
string str = "ShaXiang";
vector<char> cVector;
cVector.assign(str.begin(), str.end());
for (auto v : cVector)
{
cout << v << " "; // S、h、a、X、i、a、n、g
}
cout << endl;
No.3 vector接口的模拟实现
No.1 实现测试一:
模拟实现方式为个人练习所用,理解底层的实现方式:由于和STL的官方库会冲突,所以代码我们卸载Vector.h中的ShaXiang命名空间下,这样方便测试,也不会和标准的官方库发生冲突。
#include<iostream>
#include<cassert> // ...这里后面还会增加头文件,先用着...
using namespace std; // 官方标准stl库
// 模拟实现Vector容器的命名空间。
namespace ShaXiang
{
template<class T>
class Vecitor
{
public:
// 泛型《typedef》指针。
typedef T* iterator;
private:
iterator _start; // 存储指针
iterator _finish; // 存储大小指针
iterator _endofstorage; // 容量指针
};
}
3.1.1 构造函数的模拟实现
/* 无参构造函数 */
// Vector()
Vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
// ..
}
/* 使用迭代器的构造函数 */
template <class inputIterator>
Vector(inputIterator begin, inputIterator end)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
while (begin < end)
{
pushBack(*begin);
++begin;
}
}
/* 使用迭代器的构造函数 */
Vector(int n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
// 先开辟指定的空间,虽然并不能提高多少效率。
Reserve(n);
for (int i = 0; i < n; ++i)
{
PushBack(val);
}
}
No.2 实现测试二:
3.2.1 尾插模拟实现
先做出尾插函数方便后面测试,要不然都测试不效果..............我这里直接测试我已经写好的,里面相关的代码这一块都会进行解析,如果看不明白就继续往下看:
/* 尾插 */
// void push_back (const value_type& val);
void PushBack(const T& val)
{
// 检查扩容.
if (_finish == _endofstorage)
{
// 扩容:我们不知道要扩容多少,这里我们写一个判断:
size_t newCapacity = Capacity() == 0 ? 4 : Capacity() * 2;
Reserve(newCapacity);
}
// 程序走到这里说明:扩容完成,即扩容成不成功在Reserve中进行判断。
// 直接插入到_finish位置数据,让_finish++
*_finish = val;
++_finish;
}
3.2.2 扩容实现
vector的扩容是一个很让人烦的函数,我们这个函数会实现好几遍:因为随着程序不断加大功能,普通的扩容是不能满足当前的需求的,这里会涉及到深浅拷贝的问题:当然代码就是最后一个版本的函数~动动手往下翻翻哦
// 版本一:
/* 扩容 */
void Reserve(size_t n)
{
if (n > Capacity())
{
// No.1 先开辟新的空间.
T* temp = new T[n];
// 如果是空的容器,不需要进行拷贝,直接使用指针就可以啦
if (_start != nullptr)
{
// No.2 将原有的数据拷贝到新的内存空间中.
memcpy(temp, _start, sizeof(T) * Size());
// No.3 销毁掉之前的空间.
delete[] _start;
}
// No.4 使用新的空间
_start = temp;
_finish = Size();
_endofstorage = _start + n;
}
}
// 这个版本的程序我们直接运行会发现系统出现了访问错误的问题:那我们看看究竟是怎么回事:
我对代码进行了调试,这里我观察到_finish这个指针出现了问题:然后我更改了一个版本《解决》!
/* 扩容 */
void Reserve(size_t n)
{
if (n > Capacity())
{
// 更改:需要记录下_finish的位置,更新完容量后_finish也需要更新位置。
size_t memaryFinishPos = Size();
// No.1 先开辟新的空间.
T* temp = new T[n];
// 如果是空的容器,不需要进行拷贝,直接使用指针就可以啦
if (_start != nullptr)
{
// No.2 将原有的数据拷贝到新的内存空间中.
// memcpy(temp, _start, sizeof(T) * memaryFinishPos);
// 更改为深拷贝!
for (size_t i = 0; i < memaryFinishPos; ++i)
{
temp[i] = _start[i];
}
// No.3 销毁掉之前的空间.
delete[] _start;
}
// No.4 使用新的空间
_start = temp;
_finish = _start + memaryFinishPos;
_endofstorage = _start + n;
}
}
3.2.3 调整数据大小
// 扩容 或 扩充数据 或 删除数据。
// n > endofstorage 扩容+填数据.
// finish < n <= endofstorage 填数据.
// n <= endofstorage 删数据.
void Resize(size_t n ,const T val = 0)
{
// 扩容的大小比原来的容量大->扩容
if (n > Capacity())
{
// 扩容
Reserve(n);
}
// 这里是需要填充数据的,但是不要担心容量不足,因为上面已经会进行判断扩容啦
// 这里如果没有执行上面的if语句,说明n肯定是小于容量的。
if (n > Size())
{
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
No.3 实现测试三:
3.3.1 打印函数的实现
打印方式我写了三种:是怎么实现的呢?
这里看一下迭代器和operator[]取位置数据<就可以明白了>
// No.1 打印方式一
for (size_t i = 0; i < iVector.Size(); ++i)
{
cout << iVector[i] << " ";
}
cout << endl;
// No.2 打印方式二
for (auto v : iVector)
{
cout << v << " ";
}
cout << endl;
// No.3 打印方式三
Vector<int>::iterator itBegin = iVector.begin();
while (itBegin < iVector.end())
{
cout << *itBegin << " ";
++itBegin;
}
cout << endl;
3.3.2 迭代器
/* 迭代器:返回容器第一个位置的指针 */
iterator begin()
{
return _start;
}
const_iterator begin() const
{
return _start;
}
/* 迭代器:返回容器最后一个位置的指针 */
iterator end()
{
return _start + Size();
}
const_iterator end() const
{
return _start + Size();
}
3.3.3 operator[]取位置数据
这个代码就先不解释了,返回pos位置的别名,别名的使用一定注意:使用别名要确保空间还是存在的,否则就会和野指针一样!当然野引用和野指针是两个概念,但是意思是一致的。
/* 返回指定位置的数据 */
T& operator[](size_t pos)
{
return _start[pos];
}
const T& operator[](size_t pos) const
{
return _start[pos];
}
No.4 实现测试四:
3.4.1 指定位置插入数据函数实现
这个程序是错的,下面图解后才是对的!
/* 在pos位置插入 */
// iterator insert (iterator position, const value_type& val);
iterator Insert(iterator pos, const T& val)
{
// 检查pos的位置是否正确
assert(pos >= _start);
assert(pos <= _finish);
// 检查是否需要扩容
if (_finish == _endofstorage)
{
// 扩容:我们不知道要扩容多少,这里我们写一个判断:
size_t newCapacity = Capacity() == 0 ? 4 : Capacity() * 2;
Reserve(newCapacity);
}
// 挪动数据。
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
// 数据挪动完了,后将这个位置插入数据。
*(end + 1) = val;
++_finish;
return pos; // 返回pos位置的数据,
}
/* 在pos位置插入 */
// iterator insert (iterator position, const value_type& val);
iterator Insert(iterator pos, const T& val)
{
// 检查pos的位置是否正确
assert(pos >= _start);
assert(pos <= _finish);
// 检查是否需要扩容
if (_finish == _endofstorage)
{
// 更改:我们需要记录pos原本在容器上是哪一个位置,然后在让他指向新开辟的容器上。
size_t memaryPos = pos - _start;
// 扩容:我们不知道要扩容多少,这里我们写一个判断:
size_t newCapacity = Capacity() == 0 ? 4 : Capacity() * 2;
Reserve(newCapacity);
// 恢复pos原本指向的位置!
pos = _start + memaryPos;
}
// 挪动数据。
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
// 数据挪动完了,后将这个位置插入数据。
*(end + 1) = val;
++_finish;
return pos; // 返回pos位置的数据,
}
3.4.2 指定位置删除数据函数实现
/* 在pos位置删除 */
// iterator erase (iterator position);
iterator Erase(iterator pos)
{
// 检查pos的位置是否正确
assert(pos >= _start);
assert(pos < _endofstorage);
if (!Empty())
{
// 迭代
iterator itBegin = pos;
while(itBegin < _finish)
{
*itBegin = *(itBegin + 1);
++itBegin;
}
}
--_finish;
return pos;
}
No.5 赋值函数的实现:
3.5.1 operator=赋值实现
这里的代码是错的!正确的代码在图解下面
/* 赋值 */
Vector<T> operator=(Vector<T> v) //函数正确
{ // 这里需要更改:所以不能用const
Swap(v);
return *this;
}
// 调用
Vector<int> iVector1;
iVector1.PushBack(1);
iVector1.PushBack(2);
iVector1.PushBack(3);
Vector<int> iVector2 = iVector1; // 代码没有问题为啥执行错误呢?
3.5.2 拷贝构造实现
/* 拷贝构造 */
Vector(const Vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
// 开辟一块新的空间,将数据拷贝到新的空间中,函数体内为this
_start = new T[v.Capacity()];
memcpy(_start, v._start, sizeof(T) * v.Size());
_finish = _start + v.Size();
_endofstorage = _start + v.Capacity();
}
/* 拷贝构造现代写法 */
Vector(const Vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
// 利用构造函数构造一个函数
Vector<T> cv(v.begin(), v.end());
Swap(cv); // cv是局部的变量 ,出函数就会调用自己的析构,所以不需要做处理。
}
No.6 其他函数实现:
3.6.1 判断容器是否为空
/* 判断容器是否为空 */
bool Empty()
{
return _start == _finish;
}
/* 清空容器 */
void Clear()
{
_finish = _start;
}
/* 交换容器 */
void Swap(Vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
练习题验证
题目链接:118. 杨辉三角 - 力扣(Leetcode)
class Solution {
public:
vector<vector<int>> generate(int numRows)
{
// 定义二维的vector :就等于是二维数组。
vector<vector<int>> vv;
vv.resize(numRows); // 开辟外围vector<>的容量空间。
// 开辟内层vector<vector<int>>的容量。
for(size_t i = 0; i<vv.size(); ++i)
{
// 初始化每一个vector<int>的数据,第一个和最后一个初始化为1.
vv[i].resize(i + 1, 0);
vv[i][0] = 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++)
{
if(vv[i][j] == 0)
vv[i][j] = vv[i-1][j] + vv[i-1][j-1];
}
}
return vv;
}
};