🏆个人主页:企鹅不叫的博客
? 🌈专栏
?? 博主码云gitee链接:代码仓库地址
?若有帮助可以【关注+点赞+收藏】,大家一起进步!
💙系列文章💙
【初阶与进阶C++详解】第一篇:C++入门知识必备
【初阶与进阶C++详解】第二篇:C&&C++互相调用(创建静态库)并保护加密源文件
【初阶与进阶C++详解】第三篇:类和对象上(类和this指针)
【初阶与进阶C++详解】第四篇:类和对象中(类的六个默认成员函数)
【初阶与进阶C++详解】第五篇:类和对象下(构造+static+友元+内部类
【初阶与进阶C++详解】第六篇:C&C++内存管理(动态内存分布+内存管理+new&delete)
【初阶与进阶C++详解】第七篇:模板初阶(泛型编程+函数模板+类模板+模板特化+模板分离编译)
【初阶与进阶C++详解】第八篇:string类(标准库string类+string类模拟实现)
💎一、vector使用
🏆1.vector定义
vector()(重点) | 无参构造 |
---|
vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val | vector (const vector& x); (重点) | 拷贝构造 | vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
int main ()
{
vector<int> first;
vector<int> second(4, 100);
vector<int> third(second.begin(), second.end());
vector<int> fourth(third);
vector<string>v1;
v1.push_back("你好!");
string s = "hello!";
vector<char>v6(s.begin(), s.end());
return 0;
}
🏆2.vector iterator 的使用
函数名称 | 功能说明 |
---|
operator[] (重点) | 返回pos位置的字符,const string类对象调用 | begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 | rbegin + rend | rbegin获取最后一个字符的迭代器 + rend获取第一个字符位置的迭代器 | 范围for | C++11支持更简洁的范围for的新遍历方式 |
int main ()
{
for (size_t i = 0; i < v.size(); ++i)
{
v[i] += 1;
}
vector<int>::iterator it = v.begin();
while (it != v.end())
{
*it -= 1;
cout << *it << " ";
++it;
}
for (auto e : v)
{
cout << e << " ";
}
return 0;
}
🏆3.迭代器失效问题
迭代器底层是一个指针,迭代器失效本质是迭代器底层对应指针所指向的空间被销毁了,有以下情况
1.扩容,增容,导致原有的空间被释放(但是迭代器指针依旧指向原空间变成野指针),比如:resize、reserve、insert、assign、 push_back等 。
void TestVector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.insert(it, 99);
it++;
}
it++;
}
for (auto ch : v)
{
cout << ch << " ";
}
cout << endl;
2.erase导致数据删除,迭代器指向的位置意义变了
void TestVector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);
++it;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
总结: 使用迭代器前记得对迭代器赋值,可以避免迭代器失效的问题产生。
💎二、vector模拟实现
🏆1.vector无参构造函数/析构函数/用n个val进行构造(注意重载)
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* iterator;
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{}
~vector()
{
if (_start)
{
delete[]_start;
_start = _finish = _endofstoage = nullptr;
}
}
vector(size_t n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
vector(int n, const T& val = T())
:_start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{
resize(n, val);
}
private:
iterator _start;
iterator _finish;
iterator _endofstoage;
};
如果我们只实现==vector(size_t n, const T& val = T())==这个版本不可以,如果出现以下情况
vector<char> v1(10,'x')
vector<int> v2(10,11)
第一个会去匹配vector(size_t n, const T& val = T()),因为类型符合
第二个回去匹配vector(InputIterator frist, InputIterator last),因为他们类型最相似,一个是(int, int),另一个是模板参数,然而匹配之后,由于传过去是int类型解引用就会报错,解决办法是,函数重载一个
🏆2.vector空间增长
size | 获取数据个数 |
---|
capacity | 获取容量大小 | empty | 判断是否为空 | resize(重点) | 改变vector的size | reserve (重点) | 改变vector放入capacity |
-
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的,单次增容多,浪费就会多 -
resize开空间会初始化 -
reserve只负责开辟空间,会修改原有空间 template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* iterator;
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstoage - _finish;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
iterator begin() const
{
return _start;
}
iterator end() const
{
return _finish;
}
void reserve(size_t n)
{
size_t sz = size();
if (n > capacity())
{
T* tmp = new T[n];
if(_start)
{
for (size_t i = 0; i < size(); ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
}
_finish = _start + sz;
_endofstoage = _start + n;
}
void resize(size_t n, T val = T())
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
private:
iterator _start;
iterator _finish;
iterator _endofstoage;
};
🏆3.vector增删查改
push_back(重点) | 尾插 |
---|
pop_back (重点) | 尾删 | find | 查找。(注意这个是算法模块实现,包含< algorithm >头文件) | insert | 在position之前插入val | erase | 删除position位置的数据 | swap | 交换两个vector的数据空间 | operator[] (重点) | 像数组一样访问 |
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* iterator;
iterator insert(size_t pos, const T& x)
{
assert(pos >= _start && pos <= _finish);
if (_finish == _endofstoage)
{
size_t n = pos - _start;
size_t newcapacity = capacit y() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
pos = _start + n;
}
iterator end = _finish-1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
iterator erase(size_t pos)
{
assert(pos >= _start && pos <= _finish);
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
void push_back(const T& x)
{
if (_finish == _endofstoage)
{
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
*_finish = x;
_finish++;
}
void pop_back()
{
if (_finish > _start)
{
_finish--;
}
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
void clear()
{
_finish = _start;
}
bool empty()
{
return size() == 0;
}
private:
iterator _start;
iterator _finish;
iterator _endofstoage;
};
🏆4.vector的迭代器区间构造/拷贝构造/赋值
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* iterator;
template <class InputIterator>
vector(InputIterator frist, InputIterator last)
:_start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{
while (frist != last)
{
push_back(*frist);
++frist;
}
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstoage, v._endofstoage);
}
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{
vector<T>tmp(v.begin(), v.end());
swap(tmp);
}
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
private:
iterator _start;
iterator _finish;
iterator _endofstoage;
};
🏆5.自定义类型的拷贝问题
为什么在的reserve实现中,使用赋值重载,而没有使用memcpy?
leetcode118杨辉三角
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.resize(numRows);
for(size_t i = 0; i < vv.size(); ++i)
{
vv[i].resize(i+1, 0);
vv[i][0] = 1;
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-1]+vv[i-1][j];
}
}
}
return vv;
}
};
-
目前两层vector进行嵌套]使用,外层的vector存放的是内层vector的对象。 -
如果拷贝的是自定义类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝 -
如果只用memcpy 进行浅拷贝,相当于新的空间里面存放的是的确是新的vector对象,但这些vector对象存放的却是原有数据的指针,而原本的数据已经被我们delete掉了,出现了野指针问题。
|