从零开始手写STL库–Vector部分
文章目录
- 从零开始手写STL库--Vector部分
- Vector是什么
- Vector需要包含什么函数
- 1)基础成员函数
- 2)核心功能
- 基础成员函数的编写
- 核心功能函数的编写
- 总结
Vector是什么
std::vector 是一个动态数组,它在内存中以连续的块存储元素。与静态数组相比,std::vector 允许在运行时动态调整大小,而无需手动管理内存。
Vector需要包含什么函数
应当具有:
1)基础成员函数
构造函数:初始化Vector
析构函数:释放内存,当运行结束后要摧毁这个Vector防止内存泄漏
拷贝构造函数:允许用已有的Vector创建新的Vector
拷贝赋值操作符:允许多个Vector相互赋值
2)核心功能
push_back:在vector末尾加入元素
size:获取vector的长度
get:访问vector某处的元素
pop_back:移除vector末尾的元素
insert:在vector某处插入元素
clear:清空vector
基础成员函数的编写
Vector的成员:
一个Vector应当有指向它的指针、Vector的容量和Vector目前的长度,这样才能方便调用,以及判断是否需要扩充
template <typename T>
class myVector
{
private:
T * elements;
size_t capacity;
size_t current_size;
};
那么构造函数和析构函数也就出来了,为以上三个元素赋初值,运行结束时释放elements即可
public:
myVector() : elements(nullptr), capacity(0), current_size(0){}
~myVector()
{
delete[] elements;
}
那么拷贝构造函数是不是就是:
myVector(const myVector & other) :
elements(other.elements), capacity(other.capacity), current_size(other.current_size)
那就错了,这里elements并不是真实的数组,而是数组的指针,如果这么做的话,对other的操作会影响这里
所以这里elements需要额外创造一个数组来复制它,数据一样,指针不同
myVector(const myVector & other) : capacity(other.capacity), current_size(other.current_size)
{
elements = new T[capacity]; // copy arrays of the same length
std::copy(other.elements, other.elements + current_size, elements); // fill the array and finish copying
}
拷贝赋值操作符和拷贝构造函数很像,但是要先释放当前的elements指针,防止内存泄漏
myVector &operator = (const myVector & other)
{
if(this != other)
{
delete[] elements;
capacity = other.capacity;
current_size = other.current_size;
elements = new T[other.capacity];
std::copy(other.elements, other.elements + current_size, elements);
}
return *this;
}
这里不能直接调用析构函数和拷贝构造函数,因为析构函数和拷贝构造函数的调用不会像普通函数那样直接调用,而是通过对象的生命周期管理机制自动调用
核心功能函数的编写
1、push back函数:将元素加入到vector的末尾
试想这样一个流程:初始化vector->push_back,此时vector的数组大小还是0,根本没法push进去
所以先写一个分配内存的函数
void reserve(size_t newCapacity)
{
if (newCapacity > capacity)
{
T *newElements = new T[newCapacity];
std::copy(elements, elements + current_size, newElements);
delete[] elements;
elements = newElements;
capacity = newCapacity;
}
}
这样就可以在push前检查一下是否有余量,无余量了就扩充数组内容
void push_back(const T & input)
{
if(current_size == capacity)
{
capacity ? reserve(2 * capacity) : reserve(1);
}
elements[current_size++] = input;
}
2、size函数:获取当前vector长度
size_t size()
{
return current_size;
}
3、get函数:访问vector某处的元素
这里模仿c++库中的vector,希望实现的是通过vector[index]来获取index处的元素
T & operator[](size_t index)
{
if(index >= current_size) std::cout << "Index overstep" << std::endl;
return elements[index];
}
const T & operator[](size_t index) const
{
if(index >= current_size) std::cout << "Index overstep" << std::endl;
return elements[index];
}
这里区分const和non-const是为了防止以下问题的出现:当vector本身有const时,操作符也要有const属性
4、pop back函数:移除vector末尾的元素
void pop_back()
{
if(current_size) current_size--;
}
5、insert函数:在vector某处插入元素
这里要考虑两种情况:
一是index超出了数组范围,那么要说越界
二是index没超出范围,但是数组已经满了怎么办
三就是正常的后移index后面的元素,再插入index
void insert(const size_t index, const T & input)
{
if(index > current_size) throw std::out_of_range("Index overstep");
if(current_size == capacity) reserve(capacity == 0 ? 1 : 2 * capacity);
for(size_t i = current_size; i > index; i --)
{
elements[i] = elements[i - 1];
}
elements[index] = input;
current_size ++;
}
6、clear函数:清空vector
void clear()
{
current_size = 0;
}
7、其他函数
在使用for(auto ele : myVector)时发现出现错误:此基于范围的for语句需要合适的begin函数,但未找到
所以还需要在class中定义这些信息
T* begin()
{
return elements;
}
T* end()
{
return elements + current_size;
}
const T* begin() const
{
return elements;
}
const T* end() const
{
return elements + current_size;
}
完整的代码在:Github 中,编译建议在Linux系统中,因为本人是在Ubuntu18中编写的
理论上Windows也可以运行
总结
Vector简单实现并不算难,代码也不复杂,但是真要和C++的STL正经实现相比,还是差非常多的,写下来是为了理解vector在处理数组长度不足时怎么申请内存以及释放内存