如何借助STL开发自己的容器?
STL(Standard Template Library, 标准模板库)使用容器(Container)、迭代器(Iterator)、算法(Algorithm)构建了一个强大的框架,它允许我们使用STL中的容器存储任何类型的数据,并通过迭代器来存取,或者是调用STL中的算法。那么如何设计自己的模板,使之可以借助STL的算法以达到某些便捷的效果?
设计分配器
借助allocator<T>来设计容器存储的数据类型,其中value_type即为容器中所存储的数据的类型。分配器(Allocator)负责为模板分配空间,释放空间。在此,我们不使用封装技术设计自定义的Allocator类,而是直接为模板分配空间,STL模板库在底层使用类似的方法为模板分配空间。
typedef typename allocator<T>::value_type value_type;
typedef typename allocator<T>::size_type size_type;
void init_space(size_type size){ begin_ = (iter)::operator new(size*sizeof(value_type)); end_ = begin_+size; }
迭代器
typedef value_type* iter;
迭代器实际上是value_type类型指针。
数据成员
由于设计简易vector不涉及容量方面的操作,故一个首部迭代器和一个尾部迭代器便足够。
iter begin_;
iter end_;
成员函数
主要实现以下几个函数:构造函数、插入、删除、判空、获取元素个数、[]重载。
需要注意的是 insert() 与 erase() 函数:
void insert(iter ins_iter, size_type&& value){//以右值插入为例 uninitialized_copy(ins_iter, end_, ins_iter+1);//用以将 [插入位置,结束位置) 的值复制到 [插入位置+1,新的结束位置) *ins_iter = move(value); //将 插入位置 的值更新为待插入值 value ++end_; //由于只插入了一个值,将尾部迭代器++即可,插入多个值的情况可依此设计 }
void erase(iter ers_iter){ assert(ers_iter < end_ && ers_iter >= begin_); uninitialized_copy(ers_iter+1, end_, ers_iter);//将 [删除位置+1,结束位置) 的值复制给 [删除位置,结束位置-1) (end_-1)->~T();//注意:此处一定要使用泛型的析构函数 --end_;//由于只删除了一个元素,末尾迭代器--即可,删除多个元素可依照此设计 }
完整实现
头文件:demo.h
#ifndef Demo_H #define Demo_H #include <memory> #include <iterator> #include <cassert> using namespace std; template<typename T> class Demo{ public: typedef typename allocator<T>::value_type value_type; typedef typename allocator<T>::size_type size_type; typedef value_type* iter; public: //构造函数 Demo() { } //以空间n构造 Demo(size_type n){ init_space(n); uninitialized_fill(n, value_type()); } //以n个实值构造 Demo(size_type n, const value_type& value){ init_space(n); uninitialized_fill_n(begin_, n, value); } //拷贝构造函数 Demo(const Demo& demo){ init_space(static_cast<size_type>(demo.end_ - demo.begin_)); uninitialized_copy(demo.begin_, demo.end_, begin_); } //操作符重载 const value_type& operator[](size_type idx) const{ assert(idx < size()); return *(begin_+idx); } value_type& operator[](size_type idx){ assert(idx < size()); return *(begin_+idx); } //增删改查 void insert(iter ins_iter, const size_type& value){ insert(ins_iter, value); } void insert(iter ins_iter, size_type&& value){ uninitialized_copy(ins_iter, end_, ins_iter+1); *ins_iter = move(value); ++end_; } void erase(iter ers_iter){ assert(ers_iter < end_ && ers_iter >= begin_); uninitialized_copy(ers_iter+1, end_, ers_iter); (end_-1)->~T(); --end_; } //基础部分 bool empty(){ return begin_ == end_; } size_type size(){ return static_cast<size_type>(end_ - begin_); } iter begin(){ return begin_; } iter end(){ return end_; } private: iter begin_; iter end_; //不对外用于内部调用公开的方法 void init_space(size_type size){ begin_ = (iter)::operator new(size*sizeof(value_type)); end_ = begin_+size; } }; #endif //Demo_H
测试
源文件:main.cpp
#include <iostream> #include "demo.h" #include <algorithm> int main() { using namespace std; Demo<int> demo(10, 1024); cout<<"---------size()---------- "; cout<<demo.size()<<endl; cout<<"---------end()---------- "; cout<<*(demo.end()-1)<<endl; cout<<"---------迭代器---------- "; for(auto it = demo.begin();it != demo.end();++it){ cout<<*it << " "; } cout<<" "; cout<<"--------下标运算符-------- "; for (int i = 0; i < 10; ++i) { cout<<demo[i]<<" "; } cout<<" "; demo.insert(demo.begin(), 1000); cout<<"---------插入---------- "; for(auto it = demo.begin();it != demo.end();++it){ cout<<*it << " "; } cout<<" "; demo.erase(demo.begin()); cout<<"---------删除---------- "; for(auto it = demo.begin();it != demo.end();++it){ cout<<*it << " "; } cout<<" "; *(demo.begin()) = 2;*(demo.begin()+1) = 4;*(demo.begin()+2) = 8;*(demo.begin()+3) = 16;*(demo.begin()+4) = 32;*(demo.begin()+5) = 64; demo[6] = 128;demo[7] = 256;demo[8] = 512;demo[9] = 1024; cout<<"---------赋值---------- "; for(auto it = demo.begin();it != demo.end();++it){ cout<<*it << " "; } cout<<" "; sort(demo.begin(), demo.end(), greater<int>()); cout<<"---------调用sort函数---------- "; for(auto it = demo.begin();it != demo.end();++it){ cout<<*it << " "; } cout<<" "; cout<<"---------调用max_element函数---------- "; cout<<"max value="<<*max_element(demo.begin(), demo.end())<<endl; cout<<" "; return 0; }
结果
---------size()---------- 10 ---------end()---------- 1024 ---------迭代器---------- 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 --------下标运算符-------- 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 ---------插入---------- 1000 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 ---------删除---------- 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 ---------赋值---------- 2 4 8 16 32 64 128 256 512 1024 ---------调用sort函数---------- 1024 512 256 128 64 32 16 8 4 2 ---------调用max_element函数---------- max value=1024
成功通过自定义的模板调用STL中的函数。
更进一步
这个过程中,alloctor与迭代器的设计与使用是重中之重,如何更进一步使用memory头文件中的allocator,与iterator头文件中不同种类的迭代器,是设计出更复杂更实用的容器的基石。