在C++中,STL (Standard Template Library)是必学,也是非常重要的一块内容。STL为特定场景的数据保存提供了极大的方便。同时STL也是面试官必问的问题之一。STL有着高可重用性,高性能,高移植平台和跨平台的优点。这篇博客叫STL浅谈,因为STL涉及到的内容太多了,这里只能随便介绍介绍。
STL主要由六大块构成:
vector的底层实现其实是一个数组。vector实现了变长数组的功能,它就像数组一样,可以通过索引随机访问,但是由于数据安全,向vector中随机插入元素的效率便显得比较低下。除此之外,vector还向外提供了一个size的概念,即返回当前的vector中有多少个元素。同时还有一个capacity的概念,即返回当前的vector的容量。这一变量一般由系统自动分配,调用reverse后可以认为的改变其capacity的大小。
class MyVector{
T* elements; //指向动态数组的指针
int count; //记录添加进数组的元素个数
int capacity; //记录vector的容量
};
以上的几个数据便实现了一个vector,但是vector的强大之处并不是这几个简单的数据,而是其拥有强大的接口,下面的代码实现了一些常用的接口:
#include
//定义一个自己的名空间,避免和std中的ector的关键字冲突
namespace mySTL{
template<typename T>
class MyVector{
T* elements; //指向动态数组的指针
int vecSize; //记录添加进数组的元素个数
int vecCapacity; //记录vector的容量
public:
//默认构造函数
MyVector();
//拷贝构造
MyVector(MyVector<T>& arr);
//传数组,同时指定长度的构造
MyVector(MyVector<T>& arr, int startIndex, int endIndex);
//析构函数
~MyVector();
//将元素添加到vector中
void push_back(T element);
//弹出vector中的最后一个元素
void pop_back();
//获取当前的数据元素个数
int size()const{
return this->vecSize;
}
//获取当前的vector的容量
int capacity()const{
return this->vecCapacity;
}
//改变当前的vector的容量
void reserve(int newCapacity);
//改变当前元素的个数
void resize(int newSize);
//重载改变当前元素个数的方法,涉及到默认值
void resize(int newSize, const T& element);
//改写[]符号,支持随机访问
T& operator[](int index);
//判断当前的vector是为空
bool empty(){
return 0 == this->vecSize;
}
private:
//设置一个内部方法,当现有空间不足时申请一块新的空间,内部方法用__开头
void __realloc(int newCapacity);
};
//默认构造函数
template <typename T>
MyVector<T>::MyVector(){
//默认给一个初始容量
this->vecCapacity = 5;
this->vecSize = 0;
//申请一块预留空间,大小为capacity
this->elements = new T[this->vecCapacity];
}
//传数组的方式构造
template <typename T>
MyVector<T>::MyVector(MyVector<T>& arr){
//采用拷贝构造函数构造新的vector
this->vecSize = arr.size();
this->vecCapacity = arr.capacity();
//元素值需要逐个拷贝
this->elements = new T[this->vecCapacity];
for(int i = 0; i < this->vecSize; ++i){
*(this->elements + i) = arr[i];
}
}
//传数组,同时指定长度的构造
template <typename T>
MyVector<T>::MyVector(MyVector<T>& arr, int startIndex, int endIndex){
this->vecSize = endIndex - startIndex;
this->vecCapacity = this->vecSize;
this->elements = new T[this->vecCapacity];
int index = 0;
for(int i = startIndex; i < endIndex; ++i){
*(this->elements + (index++)) = arr[i];
}
}
//析构函数
template <typename T>
MyVector<T>::~MyVector(){
//将申请的空间释放掉,否则会产生内存泄漏
delete[] this->elements;
}
//将元素添加到vector中
template <typename T>
void MyVector<T>::push_back(T element){
//先判断空间是否已经满了,如果满了则需要重新开辟空间
if(this->vecSize >= this->vecCapacity){
this->__realloc(this->vecCapacity * 2);
}
//size加1
++this->vecSize;
*(this->elements + this->vecSize) = element;
}
//弹出vector中的最后一个元素
template <typename T>
void MyVector<T>::pop_back(){
//先判断是否为空,为空则报错
if(this->empty()){
perror("vector is empty!");
exit(-1);
}
//弹出最后一个元素只需把size减掉即可,当有新的元素压入时就会覆盖掉现有元素
--this->vecSize;
}
//改变当前的vector的容量
template <typename T>
void MyVector<T>::reserve(int newCapacity){
//当传入的newCapacity比原来的大时才能重新分配空间,否则不理会
if(this->vecCapacity < newCapacity){
this->__realloc(newCapacity);
}
}
//改变当前元素的个数
template <typename T>
void MyVector<T>::resize(int newSize){
//resize则需要先判断newSize是否大于capacity, 以及是否大于原size
//申请空间,直到容量大于newSize为止
while(this->vecCapacity < newSize){
this->__realloc(this->vecCapacity * 2);
}
//如果newSize大于原size,则需要将多出的部分进行赋值
if(newSize > this->vecSize){
for(int i = this->vecSize; i < newSize; ++i){
++this->vecSize;
*(this->elements + i) = 0;
}
}
}
//重载改变当前元素个数的方法,涉及到默认值
template <typename T>
void MyVector<T>::resize(int newSize, const T &element){
while(this->vecCapacity < newSize){
this->__realloc(this->vecCapacity * 2);
}
//如果newSize大于原size,则需要将多出的部分进行赋值
if(newSize > this->vecSize){
for(int i = this->vecSize; i < newSize; ++i){
++this->vecSize;
*(this->elements + i) = element;
}
}
}
//改写[]符号,支持随机访问
template <typename T>
T& MyVector<T>::operator[](int index){
return *(this->elements + index);
}
//设置一个内部方法,当现有空间不足时申请一块新的空间,内部方法用__开头
template<typename T>
void MyVector<T>::__realloc(int newCapacity){
//当现有的预留空间不够时,分配新的空间
this->vecCapacity = newCapacity;
//申请一块新的空间
T *temp = new T[this->vecCapacity];
//将原空间中的数据搬迁过来
for (int i = 0; i < this->vecSize; ++i)
{
*(temp + i) = *(this->elements + i);
}
//将原申请的空间全部释放,避免内存泄漏
delete[] this->elements;
//让原来的指针指向新开辟的空间
this->elements = temp;
}
}
下面做个用例测试:
#include
#include "MyVector.hpp"
int main(){
mySTL::MyVector<int> vec;
std::cout << "验证push_back()函数" << std::endl;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
std::cout << "该vector当前的容量为:" << vec.capacity() << "该vector当前的大小为:" << vec.size() << std::endl;
std::cout << "验证重载的[]是否有用" << std::endl;
for(int i = 0; i < vec.size(); ++i){
std::cout << vec[i] << " ";
}
std::cout << std::endl;
std::cout << "验证pop_back()函数是否弹出最后一个元素" << std::endl;
vec.pop_back();
vec.pop_back();
vec.pop_back();
if(vec.empty()){
std::cout << "所有元素已经弹出" << std::endl;
}
//重新压入5个元素
for(int i = 1; i < 6; ++i){
vec.push_back(i);
}
std::cout << "验证是否自动重新分配了内存空间" << std::endl;
vec.push_back(6);
std::cout << vec.capacity() << std::endl;
std::cout << "验证是否重新分配了内存空间,以及是否重新更改了resize" << std::endl;
vec.reserve(37);
std::cout << "该vector的容量为:" << vec.capacity() << "该vector的大小为:" << vec.size() << std::endl;
vec.resize(22);
std::cout << "该vector的容量为:" << vec.capacity() << "该vector的大小为:" << vec.size() << std::endl;
std::cout << "验证拷贝构造函数" << std::endl;
mySTL::MyVector<int> vec2(vec);
std::cout << "新vector的容量为:" << vec2.capacity() << "新vector的大小为:" << vec2.size() << std::endl;
std::cout << "验证带值的resize" << std::endl;
int s = vec2.size();
vec2.resize(100, 100);
for(int i = s; i < vec2.size(); ++i){
std::cout << vec2[i] << " ";
}
std::cout << std::endl;
std::cout << "验证部分值的构造函数" << std::endl;
mySTL::MyVector<int> vec3(vec2, 2, 8);
std::cout << "新vector的容量为:" << vec3.capacity() << "新vector的大小为:" << vec3.size() << std::endl;
}
输出结果:
以上的代码简单的实现了一个vector的容器,当然,相比于真正的vector还相差很多。这里只做简单的介绍,毕竟vector已经可以直接调用了,不需要重复造轮子。下面稍微说一下vector的坑点。
-使用[]访问元素时要及其注意不能越界,否则不会报错,但是返回的结果绝对是未知的。在vector中有个at方法,可以控制保证不会越界访问。