菜狗程序员 C++ 万字总结,从入门到入土(中篇)
STL
vector
模拟实现
#include <iostream>
namespace Schuyler
{
// 因为 vector 容器可以存放任意类型,所以要用模板
template <class T>
class vector
{
public:
// 用迭代器包装任意类型的指针
typedef T* iterator;
typedef const T* const_iterator;
// begin 返回空间起始地址
iterator begin()
{
return _start;
}
// end 返回最后一个有效数据的下个空间的地址
iterator end()
{
return _finish;
}
// const 对象调用,只有读权限,不能写
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
// 扩容
void reserve(int n)
{
if (capacity() < n)
{
int sz = size();
// 开辟新空间
T* p = new T[n];
if (_start)
{
// 旧空间数据拷贝到新空间
for (int i = 0; i < sz;i++)
{
p[i] = _start[i];
}
}
// 释放旧空间
delete[] _start;
// 指针指向新空间
_start = p;
_finish = p + sz;
_endOfStorage = _start + n;
}
}
// 调整 size
void resize(int n, T val = T())
{
// 要求调整的 size 小于 有效数据个数,则直接移动 _finish
if (n < size())
{
_finish = _start + n;
}
else
{
// 要求的 size 大于 容量,则另外开辟一块空间
if (n > capacity())
{
reserve(n);
}
// 从数据末尾存放新的数据 val
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
// 数据尾插就是往 _finish 处添数据
void push_back(const T& val)
{
if (capacity() == size())
{
const int newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
}
*_finish = val;
++_finish;
}
// 尾删
void pop_back()
{
if (empty())
throw new _exception{
1, "容器为空" };
--_finish;
}
// 任意位置插入
void insert(iterator pos, const T& val)
{
// 判断是否需要扩容
if (_finish == _endOfStorage)
{
// 计算 pos 离起始位置的距离,方便扩容后找到新空间的 pos
int len = pos - _start;
int newCapacity = capacity() == 0 ? 4 : capacity * 2;
reserve(newCapacity);
}
pos = _start + len;
// 向后挪动数据,插入新输入
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = val;
++_finish;
}
// 删除任意位置元素
iterator erase(iterator pos)
{
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
// 构造函数给三个成员变量赋初值
vector()
{
_start = nullptr;
_finish = nullptr;
_endOfStorage = nullptr;
}
// 使用迭代器构造 vector
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
// 拷贝构造
vector(const vector<T>& v)
{
_start = new T[v.capacity()];
for (int i = 0; i < v.size(); i++)
{
_start[i] = v._start[i];
}
_finish = _start + v.size();
_endOfStorage = _start + v.capacity();
}
private:
void swap(vector<T>& v)
{
_start = v._start;
_finish = v._finish;
_endOfStorage = v._endOfStorage;
}
public:
// 赋值运算符
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
// 析构函数
~vector()
{
if (_start)
{
delete[] _start;
}
_start = _finish = _endOfStorage = nullptr;
}
// vector 索引
T& operator[](int i)
{
if (i >= size())
throw new _exception{
0, "索引错误" };
return _start[i];
}
const T& operator[](int i)const
{
if (i >= size())
throw new _exception{
0, "索引错误" };
return _start[i];
}
private:
// 指向数组空间起始位置
iterator _start;
// 指向数组空间中有效数据末尾的下一个位置
iterator _finish;
// 指向数组空间的末尾
iterator _endOfStorage;
public:
// 有效数据个数
int size() const
{
return _finish - _start;
}
// 容器空间大小
int capacity() const
{
return _endOfStorage - _start;
}
// 判断容器是否为空
bool empty()
{
return _finish == _start;
}
};
}
vector 迭代器失效问题:
引起迭代器失效的情况有两种,
- 一种是像 resize、reserve、insert、push_back 这些能改变数据空间的操作,扩容操作会使原先空间销毁,从而产生野指针,因此需要对迭代器重新赋值。
- 即使不扩容,只要发生数据挪动,迭代器原先指向的空间可能也会发生变化,从而迭代器失效,比如 erase 操作。
另一种
string
模拟实现
# include <iostream>
namespace Schuyler
{
class string
{
public:
// 四个默认成员函数
// 构造函数
string(const char* str = "")
{
if (nullptr == str)
throw new _exception{
0,"字符串指针为 nullptr"};
int size = strlen(str);
// +1 是为了保留 ‘\0’
_str = new char[size + 1];
strcpy(_str, str);
_size = size;
_capacity = size;
}
// 析构函数
~string()
{
if (_str != nullptr)
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
}
// 拷贝构造
string(const string& str)
{
_str = nullptr;
_size = _capacity = 0;
// 开辟一块和 str 一样的空间
string tmp(str._str);
// 将新空间和 _str 交换,新空间的字符指针变为 nullptr,tmp 出作用域空间释放
swap(tmp);
}
// 赋值运算符号
string& operator=(string str)
{
// 因为 str 传值类型,会拷贝构造一个临时对象,可以借临时对象来赋值,出作用域,临时对象销毁
swap(str);
return *this;
}
// 对字符串进行索引,const 对象索引返回值不能修改
const char& operator[](int i) const
{
if (i < 0)
throw new _exception{
1, "字符串索引错误" };
return _str[i];
}
char& operator