什么是STL
STL是标准模板库。
STL六大组件
容器 | 迭代器 | 算法 | 仿函数 | 适配器(adapter) | 空间配置器(allocator) |
---|---|---|---|---|---|
vector\list \deques是一种数据结构 | (如同一个指针)提供了访问容器对象的方法,而且不暴露对象的私有成员保持封装,减少了学习成本 | 操作容器中的数据的模板函数(find()函数就在算法中)与数据类型无关,高度复用 | 类比电源适配器,有了适配器就可以很好的使用数据结构(queue、stack(用vector或者list适配出来的)) | 解决空间分配 |
STL::vector模拟实现
常用接口的实现:
Insert(在某一固定位置插入元素)
Iterator Insert(Iterator pos, T& x){
assert(pos <=_finish);
size_t offset = pos - _start;
if (_finish == _endofstorage){
Expand(2 * Capcity);
pos = _start + offset;
}
Iterator end = _finish - 1;
while (pos <= end){
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
Erase(删除指定位置的元素)
Iterator Erase(Iterator pos){
assert(pos >= _start&&pos <= _finish);
while (pos+1 < _finish){
*pos = *(pos + 1);
++pos;
}
--_finish;
return pos;
}
上面函数之所以要设计返回值,是为了避免迭代器失效的问题
增容相关的函数(Reserve、Resize)
1、Reserve:意思是预留空间,一次性将空间开好,可以避免频繁的向内存申请空间,提高效率。
只影响空间
void Reserve(int n){
if (n > Capacity()){
Expand(n);
}
}
2、Resize:分三个方面来讲解:其一假如传参的n大于capacity,那么就是简单的增容,其二假如n介于_finish到_endofstorage,那么就会做一件事,将原来_finish后面的到_endofstorage这一段数据置成相应的值;其三:n小于_finish,此时容量不会变但是size会减少到n.
不仅可以增容,还可以减少有效数据的范围
void Resize(size_t n,T x=T()){
if (n <= Size()){
_finish = _start + n;
}
else{
if (n > Capacity()){
Expand(n);
}
while (_finish < _start + n){
*_finish = x;
++_finish;
}
}
}
3、自己实现的增容函数,expand()。通常是以两倍来增的,(库中是1.5倍)
**为什么增容一次增两倍呢?(面试题)
因为假如增大于两倍但是却只插入了一次数据,那么剩下的空间就全部浪费了,相反如果只增加一倍,那么就只能满足一次复制,再有其他的操作就有得开空间了,所以效率很低,权衡之下,增两倍比较合适。
void Expand(size_t n){
if (n > Capacity()){
T* tmp = new T[n];
size_t size = Size();
for (size_t i = 0; i < size; i++){
tmp[i] = _start[i];
}
delete[] _start;
_start = tmp;
_finish = _start + size;
_endofstorage = _start + n;
}
}
完整代码:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
class MyVector{
public:
typedef T* Iterator;
typedef const T* ConstIterator;
MyVector()
:_start(NULL)
, _finish(NULL)
, _endofstorage(NULL)
{}
MyVector(const MyVector<T>& v){
if (Size()< v.Size()){
delete[]_start;
_start = new T[v.Size()];
_finish = _start;
}
for (Iterator tmp = v._start; tmp != v._finish; ++tmp){
*_finsh = *tmp;
_finish++;
}
_endofstorage = _finish;
}
MyVector<T>& operator=(const MyVector<T>& v){
if (this != &v){
swap(_start, v._start);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);
}
return *this;
}
~MyVector(){
if (_start){
delete[] _start;
_start = _finish = _endofstorage = NULL;
}
}
void Expand(size_t n){
if (n > Capacity()){
T* tmp = new T[n];
size_t size = Size();
for (size_t i = 0; i < size; i++){
tmp[i] = _start[i];
}
delete[] _start;
_start = tmp;
_finish = _start + size;
_endofstorage = _start + n;
}
}
size_t Size(){
return _finish - _start;
}
size_t Capacity(){
return _endofstorage - _start;
}
void PushBack(const T& x){
if (_finish == _endofstorage){
size_t capacity = Capacity();
capacity = capacity == 0 ? 3 : 2 * capacity;
Expand(capacity);
}
*_finish = x;
++_finish;
}
//此处会有迭代器失效的问题,因为插入数据时可能会有空间不足的问题,
//那么就需要开一块更大的空间,
//然后将数据依次拷贝到新空间,在释放就空间,所以释放了
//pos原来的位置就找不到了,所以需要记住pos相对_start的位置
Iterator Insert(Iterator pos, T& x){
assert(pos <=_finish);
size_t offset = pos - _start;
if (_finish == _endofstorage){
Expand(2 * Capcity);
pos = _start + offset;
}
Iterator end = _finish - 1;
while (pos <= end){
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
Iterator Erase(Iterator pos){
assert(pos >= _start&&pos <= _finish);
while (pos+1 < _finish){
*pos = *(pos + 1);
++pos;
}
--_finish;
return pos;
}
T& operator[](size_t pos){
assert(pos < Size());
return _start[pos];
}
//将迭代器封装两份主要是在于打印vector的时候,有时候希望只读不写,
//有时候希望读的时候改它,所以根据是否是const迭代器来判断使用哪个
Iterator Begin(){
return _start;
}
Iterator End(){
return _finish;
}
ConstIterator Begin() const{
return _start;
}
ConstIterator End() const{
return _finish;
}
void PopBack(){
if (_start != _finish){
--_finish;
}
}
//只对空间有影响
void Reserve(int n){
if (n > Capacity()){
Expand(n);
}
}
//n小于size的时候会缩小size,大于capacity扩容,两者之间会在原来数据的后面追加默认值(n-size)个
void Resize(size_t n,T x=T()){
if (n <= Size()){
_finish = _start + n;
}
else{
if (n > Capacity()){
Expand(n);
}
while (_finish < _start + n){
*_finish = x;
++_finish;
}
}
}
private:
//使用迭代器包裹私有成员,降低学习成本
Iterator _start;
Iterator _finish;
Iterator _endofstorage;
/*T* _start;
T* _finish;
T* _endofstorage;*/
};
void PrintVector(const MyVector<int>& v1){
MyVector<int>::ConstIterator it1 = v1.Begin();
//我们会发现此处报错,因为我们希望vector只被读,但是由于迭代器的原因,会将我们的const 传给非const
while (it1 < v1.End()){
cout << *it1 << " ";
++it1;
}
cout << endl;
}
void PrintVector2(MyVector<int>& v1){
MyVector<int>::Iterator it1 = v1.Begin();
while (it1 < v1.End()){
*it1 += 4;
cout << *it1 << " ";
++it1;
}
cout << endl;
}
void TestVector(){
MyVector<int> v1;
v1.PushBack(1);
v1.PushBack(2);
v1.PushBack(3);
v1.PushBack(4);
v1.PushBack(5);
v1.PushBack(6);
PrintVector(v1);
MyVector<int>::Iterator it = v1.Begin();
while (it != v1.End()){
if (*it % 2 == 0){
v1.Erase(it);
}
else{
++it;
}
}
PrintVector(v1);
/*cout << "size:" << v1.Size() << endl;
cout << "capacity:" << v1.Capacity() << endl;
v1.Reserve(12);
PrintVector(v1);
cout << "size:" << v1.Size() << endl;
cout << "capacity:" << v1.Capacity() << endl;
v1.Resize(12);
PrintVector(v1);
PrintVector2(v1);*/
}