C++实现简单版本的vector/stack/queue
一、vector
只实现标准的顺序表vector容器,不涉及算法和迭代器。
核心思想就是通过new操作得到的动态数组。
new的是容量大小的空间,_size是操作的数量。
类定义如下。
#include <iostream>
using namespace std;
#define DEFAULT_CAPACITY 10 //默认容量
template<typename _Tp>
class Vector
{
private:
//定义一些基本的成员变量
_Tp* _data = nullptr;//指向_Tp类型元素的指针,旨在借助动态数组实现Vector
size_t _size = 0;//保存元素的个数
size_t _capacity = DEFAULT_CAPACITY;//容量,即当前动态数组的大小
public:
//构造:普通构造、拷贝构造、移动构造
//赋值:拷贝赋值、移动赋值
//数组操作:尾部增加,删除某索引,获取某索引,获取元素个数
explicit Vector(size_t capacity = DEFAULT_CAPACITY);
//省略下面两个构造和两个赋值的实现
//Vector(const Vector<_Tp>& other);
//Vector(Vector<_Tp>&& other);
//Vector(const Vector<_Tp>& other);
//Vector(Vector<_Tp>&& other);
void push_back(const _Tp& element);
void remove_index(size_t index);
_Tp& at(size_t index);
const _Tp& at(size_t index) const;//上者的const重载
size_t size() const;
~Vector();
private:
//这是最核心的操作,根据当前元素的数量,对数组进行扩展
void extend();
};
具体函数实现如下。
template<typename _Tp>
void Vector<_Tp>::extend()//这是vector底层实现最关键的空间分配操作
{
//没有满就不用扩展
if (_size < _capacity) return;
//否则就要进行两倍扩容,暂存以前的数据。然后获取两倍空间,然后拷贝过去。
_capacity <<= 1;
_Tp* oldData = _data;
_data = new _Tp[_capacity];
for (int i = 0; i < _size; i++) _data[i] = oldData[i];
delete[] oldData;//释放原本的内存空间
}
template<typename _Tp>
Vector<_Tp>::Vector(size_t capacity)
{
_capacity = capacity;
_data = new _Tp[capacity];
}
template<typename _Tp>
Vector<_Tp>::~Vector()
{
//清除动态数组的所有元素
if (_data)
{
delete[] _data;
}
}
template<typename _Tp>
void Vector<_Tp>::push_back(const _Tp& element)
{
//首先检查是否需要扩展
extend();
_data[_size] = element;//直接加载_data的尾部
++_size;//更新_size
}
template<typename _Tp>
void Vector<_Tp>::remove_index(size_t index)
{
//把index后面的元素往前移动
for (int i = index; i < _size + 1; ++i)
{
_data[i] = _data[i + 1];
}
--_size;
}
template<typename _Tp>
_Tp& Vector<_Tp>::at(size_t index)
{
return _data[index];
}
template<typename _Tp>
const _Tp& Vector<_Tp>::at(size_t index) const
{
return _data[index];
}
template<typename _Tp>
size_t Vector<_Tp>::size() const
{
return _size;
}
测试如下。
int main()
{
Vector<int> vec;
for (int i = 0; i < 21; ++i)
{
vec.push_back(i);
cout << vec.at(i) << endl;
}
vec.remove_index(17);
for (int i = 0; i < vec.size(); i++)
{
cout << vec.at(i) << ", ";
}
cout << endl;
return 0;
}
二、stack
栈的主要特点是先进后出。
主要内容就是定义了一组操作,和底层数据结构关联不大。因此使用上述实现的Vector。
主要操作:入栈、出栈、返回栈顶、是否为空。
类定义和具体实现如下。
#include <iostream>
#include "my_vector.h"
using namespace std;
template<typename _Tp>
class Stack
{
private:
Vector<_Tp> _data;//vector的尾部就是stack的顶部
/*
0 ______________ size-1 (栈顶)
|______________
*/
public:
void push(const _Tp& val)
{
_data.push_back(val);
}
_Tp pop() //先暂存再删除,最后返回暂存
{
_Tp val = _data.at(_data.size() - 1);
_data.remove_index(_data.size() - 1);
return val;
}
const _Tp& top() const
{
return _data.at(_data.size() - 1);
}
bool empty() const
{
return _data.size() == 0;
}
};
测试如下。
int main()
{
Stack<int> st;
st.push(1);
cout << "top:" << st.top() << endl;
return 0;
}
三、queue
先进先出,主要内容就是定义了一组操作,和底层数据结构关联不大。
这里不使用上述实现的Vector,因为queue是队尾进,队头出。
如果使用前面实现的Vector,入队还好,出队会造成大量的元素移动操作。
两个指针分别是begin和end。
采用循环利用的方式,出队之后的元素并不移动,空出的位置就作为接下来入队元素的备用空间。
循环利用的核心操作就是取余运算,既能省时间又避免了出队后空出的空间不再被利用造成的空间的浪费。
核心操作动态扩容的策略和前面的一样,都是扩充两倍。
主要操作:入队、出队、返回队顶、是否为空。
类定义如下。
/*
0(队头) ______________ size-1 (队尾)
______________
*/
#include <iostream>
using namespace std;
//#define DEFAULT_CAPACITY2 10 //默认容量
template<typename _Tp>
class Queue1
{
private:
_Tp* _data = nullptr;//动态数组指针
int _begin = 0;
int _end = 0;
//size_t _capacity = (size_t)DEFAULT_CAPACITY2;
size_t _capacity = (size_t)10;
public:
Queue1();
~Queue1();
void push(const _Tp& element);
void pop();
const _Tp& front() const;
size_t size() const;
bool empty() const;
private:
//这是最核心的操作,根据当前元素的数量,对数组进行扩展
void extend();
};
具体函数实现如下。
template<typename _Tp>
Queue1<_Tp>::Queue1()
{
_data = new _Tp[_capacity];
}
template<typename _Tp>
Queue1<_Tp>::~Queue1()
{
if (_data) delete[] _data;
}
template<typename _Tp>
size_t Queue1<_Tp>::size() const
{
return (_end - _begin + _capacity) % _capacity;
}
template<typename _Tp>
bool Queue1<_Tp>::empty() const
{
return _begin == _end;
}
template<typename _Tp>
const _Tp& Queue1<_Tp>::front() const
{
return _data[_begin];
}
template<typename _Tp>
void Queue1<_Tp>::push(const _Tp& element)
{
extend();
_data[_end] = element;
_end = (_end + 1) % _capacity;
}
template<typename _Tp>
void Queue1<_Tp>::pop()
{
if (!empty())//不空才出队列
{
_data[_begin].~_Tp();
_begin = (_begin + 1) % _capacity;//循环后移
}
}
template<typename _Tp>
void Queue1<_Tp>::extend()
{
size_t size_one = size();
if (size_one == _capacity - 1)
{
_Tp* tmp = new _Tp[_capacity << 1];
for (int i = _begin, j = 0; i != _end; i = (i + 1) % _capacity, ++i)
{
tmp[j] = _data[i];
}
_begin = 0;
_end = size_one;
delete[] _data;
_data = tmp;
_capacity <<= 1;
}
}
测试如下。
int main()
{
Queue1<int> que;
que.push(1);
cout << "front:" << que.front() << endl;
cout << "size:" << que.size() << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!