文章目录
框架和编译冷知识
- 看别人代码,两成核心,熟悉框架
- 写一项测试一下。
- 头文件会在源文件里面展开,调用函数时只会向上去全局空间找 不会去命名空间找,不会去类域里面找。
- 某个头文件使用了cin,则应该将cin在它上面展开。
- 分区域断点调试
vector
底层还是顺序表:
构造–>尾插 -->开n个空间reserve -->支持对象【】遍历数据------>支持迭代器遍历----->拷贝构造 -->赋值重载—>resize---->支持用迭代器区间构造---->----->任意位置的插入和删除。
- 写单个函数时,思考要用到哪些成员变量或者已知条件,哪些成员变量要改变,又怎样的改变;和参数之间的成员变量又怎样的交互;是否可以借助第三方完成等等
构造,尾插,reserve、【】遍历、迭代器
基建、无参构造
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
:_start(nullptr)
, _finsh(nullptr)
, _endofstorage(nullptr)
{}
private:
iterator _start;
iterator _finsh;
iterator _endofstorage;
};
析构
~vector()
{
//释放空间以及置空
if (_start)
{
delete[] _start;
}
_start = _finish = _endOfStorage = nullptr;
}
个数、容量、判空
//计算数组元素个数
size_t size()const
{
return _finsh - _start;
}
//计算数组容量大小
size_t cap()const
{
return _endofstorage - _start;
}
//数组判空
bool empty()
{
return size() == 0;
}
push_back
void push_back(const T& x)
{
if (_finsh == _endofstorage)
{
size_t newcap = cap() == 0 ? 5 : cap() * 2;
reserve(newcap);
}
*_finsh = x;
++_finsh;
//insert(end(), x);
}
reserve
void reserve(size_t n)
{
if (n > capacity())
{
//扩容会重新开辟空间,导致成员变量为nullptr,这里记录下必要数据
size_t sz = size();
iterator tmp = new T[n];
if (_start)
{
for (size_t i = 0; i < sz; i++)
{
//对于内置类型进行赋值,对于自定义类型,调用其复制重载函数(如string)
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endOfStorage = _start + n;
}
}
Operator[]
T& operator[](size_t n)
{
assert(n < size());
return *(_start + n);
}
const T& operator[](size_t n)const
{
assert(n < size);
return *(_start + n);
}
迭代器
- 迭代器,对指针的重命名;或对指针的封装
iterator begin() {return _start;}
const_iterator begin()const { return _start; }
iterator end() { return _finsh; }
const_iterator end()const { return _finsh; }
拷贝构造、赋值重载的多种写法
拷贝构造1.0
---//v1(v2)
vector(const vector<T>& type)
{
/v1开一个和v2一样大空间
_start = new T[type.cap()];
/把v2空间的数据拷贝给v1
memcpy(_start, type._start, sizeof(T) * type.size());
/更新v1 关于有效数据个数的地址
_finsh = _start + type.size();
/更新V1的容量的地址
_endofstorage = _start + type.cap();
}
拷贝构造2.0
- v1(v2)用v2初始化v1,v1刚创建时。
vector(const vector<T>& v)
:_start(nullptr)
, _finsh(nullptr)
, _endofstorage(nullptr) /先初始化我自己
{
reserve(v.cap()); /开一个和v2一样大的空间,有空间就可以搞事情了
for (const auto& e : v) /对v2数组每个元素的引用尾插到我这个数组里
{
push_back(e);
}
}
赋值重载 =
- v1=v2 , v2实参传给形参tp,形参是实参的一份临时拷贝(重开空间存数据),所以tp是v1的深拷贝
vector<T>& operator=(vector<T> tp)
{
swap(tp);
return *this;
}
void swap( vector<T>& type)
{
std::swap(_start, type._start);
std::swap(_finsh, type._finsh);
std::swap(_endofstorage, type._endofstorage);
}
resize、迭代器区间构造
resize
void resize(size_t n, const T& value = T()) /给缺省初始化
{
if (n<=size())
{
_finsh = _start + n; /反正后面的你也访问不到
}
else
{
if (n > cap())
{
reserve(n);
}
while (_finsh<_start+n)
{
*_finsh = value;
++_finsh;
}
}
}
迭代器区间构造
- 模板类中可以存在模板函数。。
template<class Inputiterator>
vector(Inputiterator first, Inputiterator last)
:_start(nullptr)
, _finsh(nullptr)
, _endofstorage(nullptr)
{
while (first!=last)
{
push_back(*first);
++first;
}
}
插入、删除
插入
iterator insert(iterator pos, const T& val)
{
assert(pos >= begin() && pos <= end());
if (_finsh == _endofstorage)
{
size_t len = pos - _start; /记录pos的相对位置
size_t newcap = cap() == 0 ? 5 : cap() * 2;
reserve(newcap);
pos = begin() + len;
}
vector<T>::iterator it = end();
while (it>=pos)
{
*it = *(it - 1);
--it;
}
*pos = val;
_finsh++;
return pos; /返回新位置的迭代器
}
删除
iterator earses(iterator pos) //删除一个,删除多个就是resize了
{
//返回指向删除数据的下一个数据位置
assert(pos >= begin() && pos <= end());
auto it = pos;
while (it!=_finsh)
{
*it = *(it + 1);
++it;
}
--_finsh;
return pos;
}
迭代器失效
find某个值,返回该值的迭代器,然后进行插入,如果遇到增容,则该迭代器原空间可能会被销毁,再用迭代器可能造成野指针访问,
或者原空间没有销毁,但指向空间的值改变了。
Bug修正:reserve时memcpy深度的浅拷贝问题
void reserve(size_t n)
{
if (n>cap())
{
size_t sz = size(); /记录原finsh相对于start的距离
T* tmp = new T[n];
if(_start)
{
memcpy(tmp, _start,sizeof(T)*sz);
delete{} _start;
//假设这里T是string 对象 ,清理时,则会先调用string对象的析构,再释放_start
}
_start = tmp;
_finsh = _start + sz;
_endofstorage = _start + n;
}
}
int main()
{
vector<string> v1;
reserve(2);
v1.push_back("11111");
v1.push_back("11111");
v1.push_back("11111");//扩容 memcpy 后释放 已经清理了string 的_str
return 0;
}//v 走到这里时,会再次调用string 的析构_str ,释放了两次
-------------修正-------------
void reserve(size_t n)
{
if (n>cap())
{
size_t sz = size(); //记录原finsh相对于start的距离
T* tmp = new T[n];
if(_start)
{
for(size_t i=0;i<size();++i)
{
tmp[i]=_start[i]
//如果是string 对象,则是调用对象的深拷贝,
delete[] _start;
}
}
_start = tmp;
_finsh = _start + sz;
_endofstorage = _start + n;
}
}