注意
实现仿cplus官网的的string类,对部分主要功能实现
实现
文件
#pragma once
#include <string>
#include <assert.h>
namespace myvector
{
template <class T>
class vector
{
public:
//iterator
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
//constructor
//default
vector()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
//fill
//匿名对象加const引用生命周期延长至变量结束
vector(size_t n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
Reserve(n);
for (size_t i = 0; i < n; i++)
{
PushBack(val);
}
}
//重载int版,防止调用迭代器区间
vector(int n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
Reserve(n);
for (int i = 0; i < n; i++)
{
PushBack(val);
}
}
//range
template <class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr) //可以使用默认值,不用初始化列表
{
//迭代器必须用不等于,因为list可能后边的指针更小
while (first != last)
{
PushBack(*first);
first++;
}
}
//copy 实现深拷贝,不然会浅拷贝
vector(const vector<T>& x)
{
//Reserve(x.Capacity());
_start = new T[x.Capacity()];
//深拷贝,不能用memcpy,避免T是结构仍浅拷贝
//memcpy(_start, x._start, sizeof(T) * x.Size());
for (size_t i = 0; i < x.Size(); i++)
{
_start[i] = x._start[i];
}
_finish = _start + x.Size();
_end_of_storage = _start + x.Capacity();
}
//add
void PushBack(const T& x)
{
if (_finish == _end_of_storage)
{
size_t capacity = Capacity() == 0 ? 4 : Capacity() * 2;
Reserve(capacity);
}
*_finish = x;
_finish++;
}
iterator Insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _end_of_storage)
{
size_t sz = pos - _start;
//扩容后的迭代器失效问题,更新pos
Reserve(Capacity() == 0 ? 4 : Capacity() * 2);
pos = _start + sz;
}
iterator end = _finish;
while (end > pos)
{
*end = *(end - 1);
end--;
}
*pos = x;
_finish++;
return pos;
}
//de
void PopBack()
{
assert(!Empty());
_finish--;
}
iterator Erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator end = pos;
while (end != _finish)
{
*end = *(end + 1);
end++;
}
_finish--;
return pos;
}
//search
T& operator[](size_t pos)
{
assert(pos < Size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < Size());
return _start[pos];
}
//size capacity
size_t Size() const
{
return _finish - _start;
}
bool Empty() const
{
return Size() == 0;
}
size_t Capacity() const
{
return _end_of_storage - _start;
}
void Reserve(size_t n)
{
if (n > Capacity())
{
T* tmp = new T[n];
size_t size = Size();
if (_start)
{
//memcpy(tmp, _start, Size() * sizeof(T));
for (size_t i = 0; i < Size(); i++)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + size;
_end_of_storage = _start + n;
}
}
//类型()调默认构造,内置类型也可以
//首先判断设置的大小是否大于当前size,大于就需要设置参数的数据,同时还考虑扩容
void Resize(size_t n, T x = T())
{
if (n > Size())
{
if (n > Capacity())
{
Reserve(n);
}
while (_finish != _start + n)
{
*_finish = x;
_finish++;
}
}
_finish = _start + n;
}
//destructor
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
}
测试
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include "vector.h"
using namespace myvector;
//void fun(const vector<int>& x)
//{
// for (auto ch : x)
// {
// std::cout << ch << std::endl;
// }
//}
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.Resize(numRows, vector<int>());
for (size_t i = 0; i < vv.Size(); ++i)
{
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;
}
};
//vector<int> fun2()
//{
// vector<int> s;
// return s;
//}
int main()
{
vector<int> v1;
v1.PushBack(1);
v1.PushBack(2);
v1.PushBack(8);
v1.PushBack(3);
v1.PushBack(10);
v1.PushBack(6);
v1.PushBack(1);
v1.PushBack(2);
v1.PushBack(8);
v1.PushBack(3);
v1.PushBack(10);
v1.PushBack(6);
/*std::vector<int> v2;
v2.push_back(1);
v2.push_back(2);
v2.push_back(8);
v2.push_back(3);*/
//v1.PushBack(6);
/*vector<int>::iterator pos = std::find(v1.begin(), v1.end(), 2);
v1.Insert(pos, 3);*/
/*vector<int>::iterator pos = v1.begin() + 2;
v1.Erase(pos);
for (int i = 0; i < v1.Size(); i++)
{
std::cout << v1[i] << " ";
}*/
/*std::vector<int>::iterator pos = v2.begin() + 2;
for (int i = 0; i < v2.size(); i++)
{
std::cout << v2[i] << " ";
}
std::cout << std::endl;*/
/*fun(v1);*/
/*v1.PopBack();
v1.PopBack();
v1.PopBack();*/
//v1.Resize(8, 4);
//vector<int>::iterator pos = std::find(v1.begin(), v1.end(), 5);
/*if (pos != v1.end())
{
pos = v1.Insert(pos, 8);
}*/
//vs insert和erase后迭代器都不能再使用,强制检查
//(*pos)++;
/*std::vector<int>::iterator it = v2.begin();
while (it != v2.end())
{
if (*it % 2 == 0)
{
it = v2.erase(it);
}
else
{
it++;
}
}*/
/*std::vector<int>::iterator pos1 = std::find(v2.begin(), v2.end(), 2);
v2.erase(pos1);
pos1++;*/
/*vector<int>::iterator it2 = v1.begin();
while (it2 != v1.end())
{
std::cout << *it2 << " ";
it2++;
}*/
/*vector<int> v3(10, 5);
vector<int>::iterator it2 = v3.begin();
while (it2 != v3.end())
{
std::cout << *it2 << " ";
it2++;
}*/
//std::cout << std::endl;
//int ary[] = { 5,4,3,2,1 };
//vector<int> v4(ary, ary + 3);
//排序algorithm头文件的排序
//template <class RandomAccessIterator>
// sort(RandomAccessIterator first, RandomAccessIterator last);
//std::sort(v4.begin(), v4.end());
/*vector<int>::iterator it3 = v4.begin();
while (it3 != v4.end())
{
std::cout << *it3 << " ";
it3++;
}*/
//降序,要传模板
//template <class T> struct greater;
/*std::greater<int> g;
std::sort(ary, ary + sizeof(ary)/sizeof(ary[0]), g);
for (auto ch : ary)
{
std::cout << ch << " ";
}*/
//vector<std::string> v2(3, "111");
vector<vector<int>> v3 = Solution().generate(5);
for (size_t i = 0; i < v3.Size(); ++i)
{
for (size_t j = 0; j < v3[i].Size(); ++j)
{
std::cout << v3[i][j] << " ";
}
std::cout << std::endl;
}
vector<int> v4;
v4.Insert(v4.begin(), 4);;
return 0;
}
注意事项
vector可以存储任意类型的元素,所以用模板,用三个指针,一个记录数组的首地址,finish记录最后一个元素的下一个地址,end_of_storage记录数组范围的地址,类型是模板T类型指针
vector(size_t n, const T& val = T())
对于T类型的值给默认值,不能默认给0,有可能是自定义结构,所以需要调用对应的构造函数,内置类型也可以这样构造
拷贝构造不能用memcpy,如果对象涉及资源管理就会浅拷贝,内存泄露或崩溃,所以要用赋值走运算符重载
赋值运算符重载,避免T是结构体的浅拷贝
Insert中元素的长度在扩容前需要记录,不然会失效
迭代器失效
迭代器的作用是让算法不用关心底层数据结构,实际就是一个指针,或者是对指针封装。比如:vector的迭代器就是原生态指针T*。因此迭代器失效,实际就是迭代器对应指针的指向的空间销毁了,使用一块已经被释放的空间,造成的后果就是程序崩溃
1.会引起底层空间改变的操作,都有可能是迭代器失效,如:resize、reserve、insert、assign、pushback等
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
// v.resize(100, 8);
// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
// v.reserve(100);
// 插入元素期间,可能会引起扩容,而导致原空间被释放
// v.insert(v.begin(), 0);
// v.push_back(8);
// 给vector重新赋值,可能会引起底层容量改变
v.assign(100, 8);
/*
出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,
而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的
空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新
赋值即可。
*/
while(it != v.end())
{
cout<< *it << " " ;
++it;
}
cout<<endl;
return 0;
}
插入时扩容会导致原pos的迭代器失效,所以要记录长度更新pos
2.指定位置元素的删除操作 erase
#include <iostream>
using namespace std;
#include <vector>
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据,导致pos迭代器失效。
v.erase(pos);
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
erase删除pos位置的元素后,pos位置之后的元素会往前移,没有导致底层空间改变,理论上迭代器不会失效,但如果pos刚好是最后一个元素,删完之后就是end的位置,end是没有元素的,pos就失效了。因此删除任意位置元素vs就认为迭代器已经失效,会报错
下面是删除所有偶数的功能,第2个是正确的,通过返回值更新pos,迭代器就不会失效
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;
}
return 0;
}
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);
else
++it;
}
return 0;
}
3.liux下,g++编译器对迭代器的检测不严格,没有vs极端
// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{
vector<int> v{1,2,3,4,5};
for(size_t i = 0; i < v.size(); ++i)
cout << v[i] << " ";
cout << endl;
auto it = v.begin();
cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
// 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效
v.reserve(100);
cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会
// 虽然可能运行,但是输出的结果是不对的
while(it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
程序输出:
1 2 3 4 5
扩容之前,vector的容量为: 5
扩容之后,vector的容量为: 100
0 2 3 4 5 409 1 2 3 4 5
// 2. erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
#include <algorithm>
int main()
{
vector<int> v{1,2,3,4,5};
vector<int>::iterator it = find(v.begin(), v.end(), 3);
v.erase(it);
cout << *it << endl;
while(it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
程序可以正常运行,并打印:
4
4 5
// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{
vector<int> v{1,2,3,4,5};
// vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
while(it != v.end())
{
if(*it % 2 == 0)
v.erase(it);
++it;
}
for(auto e : v)
cout << e << " ";
cout << endl;
return 0;
}
========================================================
// 使用第一组数据时,程序可以运行
[@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11
[@VM-0-3-centos 20220114]$ ./a.out
1 3 5
=========================================================
// 使用第二组数据时,程序最终会崩溃
[@VM-0-3-centos 20220114]$ vim testVector.cpp
[@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11
[@VM-0-3-centos 20220114]$ ./a.out
Segmentation fault
从上面是三个例子可以看出:SGI STL迭代器失效并不一定崩溃,但运行结果肯定不对,it不在begin和end范围,肯定会崩溃
4.与vector类似,string插入+扩容+erase之后,迭代器也会失效
#include <string>
void TestString()
{
string s("hello");
auto it = s.begin();
// 放开之后代码会崩溃,因为resize到20会string会进行扩容
// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
// 后序打印时,再访问it指向的空间程序就会崩溃
//s.resize(20, '!');
while (it != s.end())
{
cout << *it;
++it;
}
cout << endl;
it = s.begin();
while (it != s.end())
{
it = s.erase(it);
// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
// it位置的迭代器就失效了
// s.erase(it);
++it;
}
}
迭代器失效,对迭代器重新赋值