网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
}
#### 💫**拷贝构造**
对于拷贝构造函数,由于涉及到深浅拷贝的问题,我们这里提供传统写法与现代写法。
##### 💦传统写法
咱们先看看思路:
1. 先开辟一块与该容器大小相同的空间。
2. 然后将该容器当中的数据一个个拷贝过来即可。
3. 最后更新\_finish和\_endofstorage的值即可。
1.
//传统写法2.
//出现深浅拷贝问题
//v2(v1)
vector(const vector& v) // 拷贝构造
{
_start = new T[v.capacity()];
memcpy(_start, v._start, v.size()* sizeof(T));
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
2.
//传统写法2.
//解决深浅拷贝问题
vector(const vector& v)
{
_start = new T[v.capacity()]; //让v2开辟一块和v1一样大小的空间
for (size_t i = 0; i < v.size(); i++)
{
_start[i] = v[i];//通过循环进行赋值
}
//最后调整_finish和_end_of_storage的大小
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
在这里就会出现一个问题:出现深浅拷贝问题。
* 写法1对于数据的拷贝采用的是memcpy函数。
* 写法2对于数据的拷贝采用的是for循环进行赋值拷贝。
两者在拷贝数据的方式上对于内置类型或不需要进行深拷贝的自定义类型,完全是满足要求的,但是当vector存储的是string时,一定存在问题。
采用图解:
![](https://img-blog.csdnimg.cn/direct/3347f1a589244cf2939368b7e3bc2344.png)
存储了5个数据,每个数据都是string类,vector<string>v2(v1),v2也开辟了5个空间。
* 写法1,在memcpy下完成拷贝,但是它们却指向了同一块空间,在调用析构函数时,就会导致同一块空间释放多次,最终内存泄露。
* 写法2,它会去调用string类的赋值重载函数进行一个深拷贝。
##### 💦**现代写法**
* 使用范围for(或是其他遍历方式)对容器v进行遍历。
* 在遍历过程中将容器v中存储的数据一个个尾插过来即可。
//现代写法
// v2(v1)
vector(const vector& v) // 拷贝构造
{
reserve(v.capacity()); // 判断是否需要扩容
for (const auto& e : v)
{
push_back(e); // 拷贝尾插
}
}
#### 💫**赋值运算符重载函数**
赋值运算符重载的进行是深拷贝,是将深拷贝出来的对象与左值进行了交换。
// v1 = v3
vector& operator=(vector v) // 赋值运算重载
{
swap(v);
return *this;
}
这里也有传统写法和现代写法,博主这里是现代写法,有兴趣的小伙伴们大家可以写写传统写法。
#### 💫**析构函数**
**首先判断该容器是否为空容器。**
* 若为空容器,则无需进行析构操作。
* 若不为空,则先释放容器存储数据的空间。
**然后将容器的各个成员变量设置为空指针即可。**
代码实现:
~vector() // 析构函数(销毁)
{
if (_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
#### 🌙**迭代器相关的函数**
begin函数返回容器的首地址,end函数返回容器当中有效数据的下一个数据的地址。
iterator begin() // 返回容器的首地址
{
return _start;
}
iterator end() // 返回容器当中有效数据的下一个数据的地址
{
return _finish;
}
const对象调用begin和end函数时所得到的迭代器只能对数据进行读操作,而不能进行修改。
const_iterator begin() const // 返回容器的首地址
{
return _start;
}
const_iterator end() const //返回容器当中有效数据的下一个数据的地址
{
return _finish;
}
小试牛刀:
int main()
{
vector v{ 1,2,3,4,5 };
//范围for进行遍历
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
运行结果:
![](https://img-blog.csdnimg.cn/direct/e8253f44992c48b9a85b7337f15a7eae.png)
### 🌙**容量相关的函数**
#### 💫**size函数和capacity函数**
咱们再看看这张图:
![](https://img-blog.csdnimg.cn/direct/78172d75725f485db264e97093c1fd36.png)
这两个指针之间对应类型的数据个数,所以size可以由\_finish - \_start得到,而capacity可以由\_endofstorage - \_start得到。
size_t size() const // 返回容器当中有效数据的个数
{
return _finish - _start;
}
size_t capacity() const // 返回当前容器的最大容量
{
return _endofstorage - _start;
}
#### 💫**reserve函数**
**reserve函数:**
* 当n大于对象当前的capacity时,将capacity扩大到n或大于n。
* 当n小于对象当前的capacity时,不进行操作。
void reserve(size_t n) // 判断扩容
{
if (n > capacity())
{
size_t old = size();
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, old * sizeof(T));
delete[] _start;
}
_start = tmp;
_finish = _start + old;
_endofstorage = _start + n;
}
}
---
首先得算好增容前的数据个数,因为增容完后,就需要释放旧空间。
1.如果没有对增容前的数据个数进行记录:
![](https://img-blog.csdnimg.cn/direct/fe694a6daf714766ad43d65d734bf721.png)
2.如果增容前后的数据拷贝使用memcpy:
![](https://img-blog.csdnimg.cn/direct/e61a8fc303304a6d9e55d3a2b17e6819.png)
#### 💫**resize函数**
**resize规则:**
* 当n大于当前的size时,将size扩大到n,扩大的数据为val,若val未给出,则默认为容器所存储类型的默认构造函数所构造出来的值。
* 当n小于当前的size时,将size缩小到n。
// 判断缩括容
// T()是匿名对象,它的生命周期本来只是存在于当前行,
// 但是被const修饰以后,可以延长它的生命周期
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
while (_finish != _start + n)
{
*_finish = val;
++_finish;
}
}
}
#### 💫empty函数
通过比较容器当中的\_start和\_finish指针的指向,若所指位置相同且为空,则该容器为空。
bool empty()const
{
return _start == _finish;
}
### 🌙**增删查改相关的函数**
#### 💫**push\_back函数**
1. **首先得判断容器是否已满。**
2. **若已满则需要先进行增容。**
3. **然后将数据尾插到\_finish指向的位置。**
4. **再将\_finish++。**
void push_back(const T& x) // 尾插
{
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
*_finish = x;
++_finish;
}
#### 💫**pop\_back函数**
1. **先判断容器是否为空**
2. **若为空则做断言处理**
3. **若不为空则将\_finish--。**
void pop_back() // 尾删
{
assert(size() > 0);
–_finish;
}
小试牛刀:
//测试一
void test_vector1()
{
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
vector<int>::iterator it1 = v.begin();
while (it1 != v.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_vector1();
return 0;
}
运行结果:
####
#### 💫**insert函数**
insert函数可以在指定的pos位置插入数据。
1. **在插入数据前先判断是否需要增容。**
2. **然后将pos位置及其之后的数据统一向后挪动一位。**
3. **最后将数据插入到pos位置。**
void insert(iterator pos, const T& x) // 某个位置插入数据
{
assert(pos >= _start && pos <= _finish);
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;
}
#### 💫**erase函数**
![img](https://img-blog.csdnimg.cn/img_convert/2c5d2425a27f7ccd5d93a4881270e415.png)
![img](https://img-blog.csdnimg.cn/img_convert/18c6b93be055714d1ac04fc0cdfe0582.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**
e(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
memmove(pos + 1, pos, sizeof(T) * (_finish - pos));
*pos = x;
++_finish;
}
💫erase函数
[外链图片转存中…(img-Ntic2ebj-1715820773285)]
[外链图片转存中…(img-48ntdsvk-1715820773285)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新