vector 实现
成员属性/迭代器
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
iterator begin()
{
return _first;
}
iterator end()
{
return _end;
}
const_iterator begin() const
{
return _first;
}
const_iterator end() const
{
return _end;
}
private:
iterator _first = nullptr;
iterator _end = nullptr;
iterator _capacity = nullptr;
};
这里的迭代器可以看到就是指针, 这只是一种实现方式
也可以创建一个 struct iterator {}; 进行一层封装.
同样的这里没有使用 size 和 capacity, 而是通过三个指针来代替
也可以使用 size 和 capacity 来实现
构造/析构/拷贝构造/赋值重载函数
vector()
{}
vector(size_t n, const T& val = T()) // T& val = T(): 缺失值写法, 没有传参就会调用 T类型 的默认构造, 创建一个对象
{
_first = new T[n];
for (int i = 0; i < n; i++)
{
_first[i] = val;
}
_end = _first + n;
_capacity = _end;
}
template<class It>
vector(It begin, It end) // 迭代器构造
{
// 这里用上模板, 因为迭代器类型有很多种, 我们不能为每一个迭代器实现具体方法,
// 所以这里用模板, 让编译器帮助我们生成对应类型的迭代器构造函数
while (begin != end)
{
push_back(*begin);
begin++;
}
}
~vector()
{
delete[] _first;
}
vector(vector<T>& v) // 拷贝构造
{
for (auto tem : v) // 复用尾插函数
{
push_back(tem);
}
}
void swap(vector<T>& v)
{
std::swap(_first, v._first);
std::swap(_end, v._end);
std::swap(_capacity, v._capacity);
}
vector<T>& operator=(vector<T> v)
{
swap(v); // v 是一个零时变量, 函数结束时, 就会被销毁, 我们可以将 v 的空间拿过来使用
// 将零时变量的空间替换过来, 这样我们就不用自己去开辟空间
return *this;
}
这里使用模板, 让编译器来完成不同类型迭代器的构造方法.
空间函数
size_t size()
{
return _end - _first;
// 两个指针相减得到的结果是它们之间的“距离”,
// 这个距离是以它们指向的元素类型的“大小”为单位的整数
}
size_t capacity()
{
return _capacity - _first;
}
void reserve(size_t n)
{
int len = size();
// 获取 vector 的size, 如果size < n, 就不做处理, 保证空间至少能存储现有数据
if (len < n)
{
T* tem = new T[n];
for (int i = 0; i < len; i++)
{
tem[i] = _first[i];
}
std::swap(_first, tem); // 空间开辟完成后, 记得修改三个指针
delete[] tem; // 原空间需要销毁, 否者造成空间泄漏
_end = _first + len;
_capacity = _first + n;
}
}
void resize(size_t n, const T& val = T())
{
int len = size();
if (len >= n)
{
_end = _first + n;
}
else
{
reserve(n); // 交给 reserve 判断是否需要开辟空间
int i = 0;
for (int i = 0; len + i < n; i++)
{
_first[len + i] = val;
}
_end = _first + n;
}
}
修改
void push_back(const T& val)
{
size_t len = size();
if (_capacity == _end) // 判断空间是否使用完了
{
reserve(len > 0 ? 2 * len : 4);
}
*_end = val;
++_end;
}
void pop_back()
{
if (size() > 0)
{
--_end;
}
}
iterator insert(iterator it, const T& val)
{
size_t len = size();
size_t x = it - _first; // 记录插入的位置
if (_capacity == _end)
{
reserve(len > 0 ? 2 * len : 4);
}
iterator end = _end;
it = _first + x; // it 需要更新, 重新分配空间后, it 失效了
while (end >= it)
{
*end = *(end - 1); // 挪动数据
--end;
}
*it = val;
++_end;
return it; // 返回插入元素的迭代器
}
iterator erase(iterator it)
{
iterator newit = it;
if (size() > 0)
{
while (it < _end - 1)
{
*it = *(it + 1);
it++;
}
}
return newit; // 返回删除元素后一个元素的迭代器
}
T& operator[](size_t pos)
{
return _first[pos];
}
const T& operator[](size_t pos) const
{
return _first[pos];
}
list 实现
ListNode 实现
list 底层是双向链表实现的. 所以节点中不仅存储下一个节点的位置, 还会存储上一个节点的位置.
template<class T>
struct ListNode
{
ListNode(const T& val = T())
:_data(val),
_next(nullptr),
_prev(nullptr)
{}
~ListNode()
{}
T _data; // 存储的数据
struct ListNode* _next; // 指向下一个节点
struct ListNode* _prev; // 指向上一个节点
};
list 成员属性
class list
{
private:
typedef struct ListNode<T> LNode;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
// 迭代器后面实现
private:
LNode* _head = nullptr;
size_t _size = 0; // 记录链表的长度
}
构造函数
list()
{
_head = new LNode;
_head->_next = _head->_prev = _head;
_size = 0;
}
template<class T>
list(T begin, T end) // 迭代器构造
{
_head = new LNode;
_head->_next = _head->_prev = _head;
_size = 0;
while (begin != end)
{
push_back(*begin);
begin++;
}
}
修改
这里插入删除和双向链表的插入删除是一样的
参考: C语言实现链表-CSDN博客
void push_back(const T& val)
{
LNode* prev = _head->_prev;
LNode* newnode = new LNode(val);
prev->_next = newnode;
newnode->_prev = prev;
_head->_prev = newnode;
newnode->_next = _head;
_size++;
}
void pop_back(const T& val)
{
if (_head->_next == _head)
{
return;
}
LNode* prev = _head->_prev->_prev;
delete _head->_prev;
prev->_next = _head;
_head->_prev = prev;
_size--;
}
void push_fornt(const T& val)
{
LNode* prev = _head->_prev;
LNode* newnode = new LNode(val);
LNode* next = _head->_next;
_head->_next = newnode;
newnode->_prev = _head;
newnode->_next = next;
next->_prev = newnode;
_size++;
}
void pop_front()
{
if (_head->_next == _head)
{
return;
}
LNode* next = _head->_next->_next;
delete _head->_next;
_head->_next = next;
next->_prev = _head;
_size--;
}
其他函数
void swap(list& x)
{
std::swap(_head, x._head);
std::swap(_size, x._size);
}
void clear()
{
LNode* cur = _head->_next;
while (cur != _head)
{
LNode* next = cur->_next;
delete cur;
next = cur;
}
_size = 0;
}
迭代器实现
list 的迭代器不能像 vector 一样, 原始指针就是当作迭代器来使用.
vector 底层就是一块连续的空间, 指针的 ++, -- 或是 * 等运算符都能直接使用.
template<class T, class Ref, class Por>
struct __list_iterator
{
typedef struct ListNode<T> LNode;
typedef __list_iterator<T, Ref, Por> self;
__list_iterator(LNode* node)
:_node(node)
{}
self& operator++() // 前置 ++ 就是向后走一个节点, 返回后一个节点的迭代器
{
_node = _node->_next;
return *this;
}
self& operator--() // 前置 -- 就是向前走一个节点, 返回前一个节点的迭代器
{
_node = _node->_prev;
return *this;
}
self& operator++(int) // 后置 ++ 就是向后走一个节点, 返回当前节点迭代器
{
LNode* tem = _node;
_node = _node->_next;
return tem;
}
self& operator--(int) // 后置 -- 就是向前走一个节点, 返回当前节点迭代器
{
LNode* tem = _node;
_node = _node->_prev;
return tem;
}
Ref operator*() // * 返回这个节点存储的数据本身, int* arr, *arr 返回一个 int 类型数据.
{
return _node->_data;
}
Por operator->() // -> 返回这个节点存储的数据的地址
{
return &_node->_data;
}
bool operator==(self& s)
{
return _node == s._node;
}
bool operator!=(self& s)
{
return _node != s._node;
}
LNode* _node;
// 底层还是 ListNode 指针
};
可以看到, 这个迭代器底层就是 ListNode* , 然后通过重载 ++, -- 等运算符来完成像 vector指针++的作用. 原始的ListNode* ++ 并不能实现向后走一个节点这样的功能, 所以需要重载.
operator-> 的特殊优化
class Data
{
public:
Data(int year = 2024, int month = 5, int day = 2)
:_year(year),
_month(month),
_day(day);
{}
int _year;
int _month;
int _day;
};
list<Data> l1;
Data d1;
l1.push_back(v1);
auto it = l1.begin();
cout << it->_year << ":" << it->_month << ":" << it->day;
当 list 中存储的是一个自定义类型时, 通过 operator-> 可以得到自定义类型的指针,
指针应该再使用 -> 来访问自定义类型的内部成员属性
这里的写法是经过了优化.
class Data
{
public:
Data(int year = 2024, int month = 5, int day = 2)
:_year(year),
_month(month),
_day(day);
{}
int _year;
int _month;
int _day;
};
list<Data> l1;
Data d1;
l1.push_back(v1);
auto it = l1.begin();
// 我们上面自己实现的
Data* operator->() // -> 返回这个节点存储的数据的地址
{
return &_node->_data;
}
cout << it->()->_year; // 未做特殊处理的写法
// 可以看到 it->() 得到了指针, 然后再通过指针来访问自定义类型内部的成员变量
这里的优化是方便了我们使用, 使用上面未作特殊处理写法也是可以的.