(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考:https://tool.oschina.net/uploads/apidocs/cpp/en/cpp/container/vector.html
参考:https://tool.oschina.net/uploads/apidocs/cpp/en/cpp/iterator/reverse_iterator.html
参考:https://tool.oschina.net/uploads/apidocs/cpp/en/cpp/concept/RandomAccessIterator.html
引言
C++代码中,STL库的使用,让我们对基本的数据结构使用很便利;
常用的 std::vector数组、std::list链表、std::map映射、std::set集合、std::basic_string字符串等。
这些数据结构的定义我们一般来说,也都是比较清楚的:
vector提供一段连续内存存储,
list提供链表结构存储,
map提供树形结构存储,
set提供树形的结构存储,
basic_string提供连续内存段存储字符。
数据结构清楚,对于stl库的实现呢,或许有时我们觉得比较简单,觉得也就那样,但真实实现起来,要做的工作也还是比较多的。
1. vector的实现分析
拿std::vector来说,我们看看它要实现方法的哪些:
- 对元素的访问,提供了at, operaotr[], iter等多种数据的访问方式,也提供了front, back等首位元素的访问方式;
- Iter方式提供了Iterator, reverse_iterator, const_iterator, const_reverse_iterator四种迭代器,分别映射不同的场景需要。iterator要支持多种operator操作,包括operator++, operator–, operator->, operator*等操作,还有operator==, operator!=判定操作。
- 构造函数要支持空置,初始复制,copy构造,operator=赋值,assign赋值;另外支持std::move右值引用的方式的copy构造,operator==赋值也要来一套。
- 数据的操作上,push_back, emplace_back, clear都要用,一方面也有std::move右值引用的处理;
- 数据的复杂操作上,insert,erase操作其实不算很特殊,对于链表结构比较简单,但对于一维整块内存的vector来说,就复杂一些了,从中间插入或删除一条数据时,涉及的后续所有元素的位移。
- 内存维护上,size增长时,capacity空间不够了,需要新申请内存块,然后把原数据copy到新块上,释放掉老的块;size减少时,如果要回收内存shrink_to_fit回收内存,申请size块,把数据copy过来,释放掉老的块。
2. 定义一个新的类模版DataStorage
下面尝试自己来实现一个模版类,来满足一些常用语法、常用接口的使用。
定义一个datastorage类,综合std::vector与std::array特点,提供一个由这两个类对象组合出来的内存结构类模版,该模版类有一个固定内存块和一个动态内存块,对外提供stl的相关访问方法。
2.1 初始结构
初始结构形如,在DataStorage中,同时拥有两个存储的变量和一个记录长度的变量;
一个变量是array_,拥有固定长度大小的数组内存块;
一个变量是vector_,可以拥有弹性的数据长度;
一个变量是size_,记录当前使用的数据长度;
template <typename T, std::size_t ArraySize>
class DataStorage {
public:// 构造函数DataStorage() : array_(), vector_(), size_(0) {}private:std::array<T, ArraySize> array_;std::vector<T> vector_;size_t size_;
};
2.2 增加访问接口:添加数据与获取数据
增加访问接口:获取数据,添加数据接口。
添加一个push_back方法,往空间里放置数据,放置时,先看看array够不够放,够放时,先往array中放数据;array满了之后,往vector中放数据;
添加一个get方法,基于要get的index位置,或是从array中取数据,或是从vector中取数据,对外提供引用,支持外部修改获取到的数据;
添加了一个operator[]的便利访问方法,使用[]方式访问数据,读取对应index数据或是修改对应index的数据。
添加size(), empty()来获取元素个数和判断。
// modifyvoid push_back(const T& value){if (size_ < array_.size())array_[size_] = value;elsevector_.push_back(value);size_++;}// access through [] and getT& operator[](size_t i){return get(i);}T& get(size_t i){if (i < array_.size())return array_[i];elsereturn vector_[i - array_.size()];} size_t size() {return size_;}bool empty() {return size_ == 0;}
2.3 添加copy构造数据
添加了copy构造处理,从而支持使用一个相同类型Storage构造另一个Storage,也支持operator=的操作。arry与vector都有响应的copy赋值操作,直接使用即可。
添加copy构造时的数据转移,添加operator赋值时的数据转移,这样就可以把一个变量数据转移到另一个变量上,减少数据的copy过程。
这块也要借用vector和array的右值引用转移处理。
// copy constructorDataStorage(const DataStorage& storage){vector_ = storage.vector_;array_ = storage.array_;size_ = storage.size_;}DataStorage& operator=(const DataStorage& storage){vector_ = storage.vector_;array_ = storage.array_;size_ = storage.size_;}// right referenceDataStorage(DataStorage&& storage){vector_ = std::move(storage.vector_);array_ = std::move(storage.array_);size_ = storage.size_;storage.size_ = 0;}DataStorage& operator=(DataStorage&& storage){vector_ = std::move(storage.vector_);array_ = std::move(storage.array_);size_ = storage.size_;storage.size_ = 0;}
2.4 添加Iterator访问支持
添加iterator访问接口支持
class Iterator{public:Iterator(DataStorage* p, const size_t idx) : data_(p), idx_(idx){}private:friend class DataStorage; size_t idx_;DataStorage* data_;};};// IteratorIterator begin(){return Iterator(this, 0);}Iterator end(){return Iterator(this, size_);}
2.5 为Iterator增加自增操作与相等判定操作
为了支持这样方式的使用 for(auto iter=xxx.begin(); iter != xxx.end(); iter++);
需要增加支持自增操作,++iter,与iter++支持,判断iter是否相等的支持 != ==。
需要增加支持*iter取值,也支持T为复杂结构时,取T中的元素->方式取值。
在Iterator类中添加定义:
// pre increment ++iter, Iterator& operator++(){idx_++;return *this;}// post increment iter++Iterator operator++(int){Iterator before = *this;idx_++;return before;}// operator = and !=bool operator==(const Iterator& it){return idx_ == it.idx_ && data_ == it.data_;}bool operator!=(const Iterator& it){return idx_ != it.idx_ || data_ != it.data_;}T& operator*(){return data_->get(idx_);}T* operator ->(){return &data_->get(idx_);}
2.6 增加reverse_iterator逆序遍历支持
通过iterator可以方便实现正序遍历,逆序遍历也一样,也可以同样进行支持
class reverse_iterator : public Iterator{public:reverse_iterator(DataStorage* p, const size_t idx) : Iterator(p, idx){}// pre increment ++i, --ireverse_iterator& operator++(){this->idx_--;return *this;}// post increment i++, i--reverse_iterator operator++(int){auto before = *this;this->idx_--;return before;}};// reverse iteratorreverse_iterator rbegin() {return reverse_iterator(this, size() - 1);}reverse_iterator rend() {return reverse_iterator(this, -1);}
3. 验证DataStorage类型
验证DataStorage是否可用,写简单的赋值、取值、遍历程序,来验证DataStroage的可用性。
- 验证放置数据push_back;
- 验证[]方式访问数据,设置数据
- 验证[]方式打印数据列表
- 验证&&右值引用方式做数据转移
- 验证iter方式访问数据,遍历数据,iter取值
- 验证=赋值方式copy数据
- 验证for each的iter方式取数据
- 逆序iter遍历支持
验证程序如下:
int main(int argc, char** argv){DataStorage<int, 3> data;for (int i=0; i<10; i++)data.push_back(i); data[9] = 9999;for (int i=0; i<data.size(); i++)printf("data[%d] = %d\n", i, data[i]);DataStorage<int, 3> other = std::move(data); for (auto iter=data.begin(); iter != data.end(); iter++)printf("*iter = %d\n", *iter);data = other;for (auto val : data)printf("each = %d\n", val); for (auto iter=data.rbegin(); iter != data.rend(); iter++)printf("*reverse_iter = %d\n", *iter);return 0;
}
4. 扩展
进一步扩展DataStorage模板类,可以考虑添加反向遍历的迭代器const_iterator、和const_reverse_iterator类;
也可以考虑添加erase方法,支持删除数据;
也可以考虑添加insert方法,从某个iter位置向前添加数据;
还可以支持一些常用接口swap,clear,shrink_to_fit相关操作。
在erase设计时,要考虑数据删除后,后续数据的前移,std::array中的部分数据前移,std::vector中的数据前移。
在insert设计时,要考虑数据插入时,数据的后移,数据后移后流出的位置放置新的数据。
erase与insert代码实现形如:
- 注意使用std::memmove做数据赋值,用于支持copy的src区域和dest区域有重合的情况。
- 另外std::memove的第三个字段是字节数量,注意个数乘以sizeof(T)来计算copy的字节数。
- 区分插入或删除位置位于array空间内还是vector空间内,vector空间内,可以直接交给vector的内部函数做处理;位于array空间内的,需要自定义处理,同时处理array空间部分和vector空间部分。
Iterator erase(Iterator& iter){if (iter.idx() < array_.size()){std::memmove(array_.data() + iter.idx(), array_.data() + iter.idx() + 1, (array_.size() - iter.idx() - 1)*sizeof(T));if (size() > array_.size()){array_.back() = vector_.front();vector_.erase(vector_.begin());}}else{auto itererase = vector_.begin() + (iter.idx() - array_.size());vector_.erase(itererase);}size_--;return iter;}Iterator insert(Iterator& iter, const T& value){if (iter.idx() < array_.size()){if (size() >= array_.size()){vector_.insert(vector_.begin(), array_.back());}std::memmove(array_.data() + iter.idx() + 1, array_.data() + iter.idx(), (array_.size() - iter.idx() - 1)*sizeof(T));*iter = value;}else{auto iteradd = vector_.begin() + (iter.idx() - array_.size());vector_.insert(iteradd, value);}size_++;return iter;}
验证代码:
for (auto iter=data.begin(); iter != data.end(); iter++)iter = data.erase(iter);printf("after erase\n"); for (auto iter=data.begin(); iter != data.end(); iter++)printf("*iter = %d\n", *iter);for (auto iter=data.begin(); iter != data.end(); iter++){iter = data.insert(iter, *iter - 1);iter++;}printf("after insert\n");for (auto iter=data.begin(); iter != data.end(); iter++)printf("*iter = %d\n", *iter);
5. 附录
附录vector支持的常用接口列表:
Member functions
func name | info |
---|---|
(constructor) | constructs the vector |
(destructor) | destructs the vector |
operator= | assigns values to the container |
assign | assigns values to the container |
get_allocator | returns the associated allocator |
Element access
func name | info |
---|---|
at | access specified element with bounds checking |
operator[] | access specified element |
front | access the first element |
back | access the last element |
data | (C++11) direct access to the underlying array |
Iterators
func name | info |
---|---|
begin/cbegin | returns an iterator to the beginning |
end/cend | returns an iterator to the end |
rbegin/crbegin | returns a reverse iterator to the beginning |
rend/crend | returns a reverse iterator to the end |
Capacity
func name | info |
---|---|
empty | checks whether the container is empty |
size | returns the number of elements |
max_size | returns the maximum possible number of elements |
reserve | reserves storage |
capacity | returns the number of elements that can be held in currently allocated storage |
shrink_to_fit | (C++11) reduces memory usage by freeing unused memory |
Modifiers
func name | info |
---|---|
clear | clears the contents |
insert | inserts elements |
emplace | (C++11) constructs element in-place |
erase | erases elements |
push_back | adds elements to the end |
emplace_back | (C++11) constructs elements in-place at the end |
pop_back | removes the last element |
resize | changes the number of elements stored |
swap | swaps the contents |
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)