【初识C++】4.2 STL-vector

1.标准库中的vector类

1.1vector 简单介绍

在这里插入图片描述

vector是表示可变数组大小的容器,和string类有很多相似的地方,这里就不赘述了

1.2 vector的构造函数

在这里插入图片描述
重点掌握1234的构造方法
为了便于观察结果,首先构造一个print_vector函数
print_vector:

void print_vector(const vector<T>& v)
{
  typename vector<T>::const_iterator it = v.begin();
  while (it != v.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;
}

构造函数:

void vector_test1()
{
  vector<int> v1;         //方法1
  v1.push_back(1);
  v1.push_back(2);
  print_vector(v1);     
  vector<int> v2(10, 0);  //方法2
  print_vector(v2);
  string str("hello world");
  vector<char> v3(str.begin(), str.end()); //方法3
  print_vector(v3);
  vector<int> v4(v1);  //方法4
  print_vector(v4);
}

allocator是内存分配器的意思,凭借现有知识还无法理解,就先跳过
方法1:调用默认构造函数,并初始化成员变量
方法2:前一个参数表示顺序表元素数量,后一个参数表示顺序表内元素初始化的值
方法3:使用迭代器,用迭代器依次插入数据
方法4:拷贝构造

1.3 vector的三种遍历方式

void vector_test2()    
{    
  vector<int> v;    
  v.push_back(1);    
  v.push_back(2);    
  v.push_back(3);    
  v.push_back(4);    
  v.push_back(5);    
  v.push_back(6);    
  for (size_t i = 0; i < v.size(); i++)     //1.operator[]
  {    
    cout << v[i] << " ";    
  }    
  cout << endl;    
    
  vector<int>::iterator it = v.begin();     //迭代器     
  while (it != v.end())    
  {    
    cout << *it << " ";                                                                                                                                                                 
    it++;    
  }    
  cout << endl;    
    
  for (auto e : v)                          //范围for
  {    
    cout << e << " ";    
  }    
  cout << endl;    
}    

和4.1STL-string类中的类似,就不展开了

1.4 vector的扩容方式

分别在两种编译器下设计小实验观察vector的增容方式
小测试:

```c
void vector_test2()
{
  size_t sz;
  vector<int> v1;
  sz = v1.capacity();
  cout<< "making v1 grow.. \n";
  for (size_t i = 0; i < 100; i++)
  {
    v1.push_back(i);
    if (sz != v1.capacity())
    {
      sz = v1.capacity();
      cout << "capacity changed:" << sz << endl;
    }
  }
}
int main()
{
  vector_test4();
  return 0;
}

1.g++

[clx@VM-20-6-centos test1_C++]$ ./test
making v1 grow.. 
capacity changed:1
capacity changed:2
capacity changed:4
capacity changed:8
capacity changed:16
capacity changed:32
capacity changed:64
capacity changed:128

结论:在Linux操作系统g++编译器下,vector扩容遵循二倍扩容、

小结:频繁扩容会降低效率,假如刚开始就知道需要空间的大致大小,就可以进行手动扩容提高效率
2.VS
在这里插入图片描述

在Vs中当capacity小于五时一个一个扩容,当capacity要大于五时,1.5倍扩容

1.5 vector的增删查改

增:
在这里插入图片描述
在这里插入图片描述
删:
在这里插入图片描述
在这里插入图片描述
查:
在这里插入图片描述
头文件:#include < algorithm >
find函数:find函数具有三个形参,前两个是迭代器,最后一个是元素的值。find函数可以通过迭代器标记区间,对区间内的元素进行遍历查找。find(v.begin(), v.end(), 3)的意思就是查找[v.begin(),v.end())区间内第一个值为3的元素,并返回它的地址

注意:vector类并没有自己的find函数,其查找函数是使用所有容器都有的迭代器进行实现,此find函数在算法模块中,当然也有rfind()等函数

1.6 迭代器失效

在实际操作中,迭代器有两种情况会失
1.意义改变
2.野指针

测试1:pos意义改变

void vector_test1()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  v.push_back(5);
  v.push_back(6);
  vector<int>::iterator pos = find(v.begin(), v.end(), 3); --找到3的位置
  v.insert(pos, 30);                     --3的位置插入30
  for (auto e : v)                       --打印顺序表
  {
    cout << e << " ";
  }
  cout << endl;
  v.erase(pos);                           --删除3
  for (auto e: v)
  {
    cout << e << " ";
  }                                        --打印顺序表
  cout << endl;
}

编译运行我们的代码

[clx@VM-20-6-centos test1_C++]$ make
make: `test' is up to date.
[clx@VM-20-6-centos test1_C++]$ ./test
1 2 30 3 4 5 6                       
1 2 3 4 5 6                           --3并没有被删除,30被删了

应该不难理解,pos找到3的位置之后,我们又在顺序表中插入了30这个数据。30后面的数据发生了平移。而原来pos指向的空间变成了30.所以就把30删掉了
本质:pos的意义发生了改变
思考:使用insert可能发生扩容,若扩容则开辟一块新空间,为什么还可以使用pos找到对应数据呢。
若函数扩容,insert函数肯定知道,它就可以自身改变pos的值重新让其指向新开辟空间的原来位置。并且insert生成的pos仅仅是实参的拷贝,所以insert中pos改变并不影响外面pos的值

根据上述思考就可以找到第二种迭代器失效情况
测试二:pos野指针问题

void vector_test2()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3); --首先先插入三个数字
  vector<int>::iterator pos = find(v.begin(), v.end(), 2); --找到2的位置
  v.push_back(4);  
  v.push_back(5);
  v.push_back(6);
  v.push_back(7); --再插入四个数据
  v.erase(pos);   --删除2
  for (auto e: v) --遍历顺序表
  {
    cout << e << " ";
  }
  cout << endl;
  
}

运行程序

[clx@VM-20-6-centos test1_C++]$ ./test
2 3 4 5 6 7 
*** Error in `./test': double free or corruption (out): 0x00000000018fec60 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x81329)[0x7fd15cbf0329]
./test[0x401f6c]
./test[0x4018ac]
./test[0x401379]
./test[0x4010c5]
./test[0x400fd0]
./test[0x400fff]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7fd15cb91555]
./test[0x400ad9]
======= Memory map: ========
00400000-00404000 r-xp 00000000 fd:01 793720                             /home/clx/Lesson_Linux/lesson_7_31/test1_C++/test
00603000-00604000 r--p 00003000 fd:01 793720                             /home/clx/Lesson_Linux/lesson_7_31/test1_C++/test
00604000-00605000 rw-p 00004000 fd:01 793720                             /home/clx/Lesson_Linux/lesson_7_31/test1_C++/test
018ed000-0191f000 rw-p 00000000 00:00 0                                  [heap]
7fd158000000-7fd158021000 rw-p 00000000 00:00 0 
7fd158021000-7fd15c000000 ---p 00000000 00:00 0 
7fd15c96b000-7fd15c96d000 r-xp 00000000 fd:01 3494                       /usr/lib64/libdl-2.17.so
7fd15c96d000-7fd15cb6d000 ---p 00002000 fd:01 3494                       /usr/lib64/libdl-2.17.so
7fd15cb6d000-7fd15cb6e000 r--p 00002000 fd:01 3494                       /usr/lib64/libdl-2.17.so
7fd15cb6e000-7fd15cb6f000 rw-p 00003000 fd:01 3494                       /usr/lib64/libdl-2.17.so
7fd15cb6f000-7fd15cd33000 r-xp 00000000 fd:01 3486                       /usr/lib64/libc-2.17.so
7fd15cd33000-7fd15cf32000 ---p 001c4000 fd:01 3486                       /usr/lib64/libc-2.17.so
7fd15cf32000-7fd15cf36000 r--p 001c3000 fd:01 3486                       /usr/lib64/libc-2.17.so
7fd15cf36000-7fd15cf38000 rw-p 001c7000 fd:01 3486                       /usr/lib64/libc-2.17.so
7fd15cf38000-7fd15cf3d000 rw-p 00000000 00:00 0 
7fd15cf3d000-7fd15cf52000 r-xp 00000000 fd:01 3501                       /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15cf52000-7fd15d151000 ---p 00015000 fd:01 3501                       /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15d151000-7fd15d152000 r--p 00014000 fd:01 3501                       /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15d152000-7fd15d153000 rw-p 00015000 fd:01 3501                       /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7fd15d153000-7fd15d254000 r-xp 00000000 fd:01 3496                       /usr/lib64/libm-2.17.so
7fd15d254000-7fd15d453000 ---p 00101000 fd:01 3496                       /usr/lib64/libm-2.17.so
7fd15d453000-7fd15d454000 r--p 00100000 fd:01 3496                       /usr/lib64/libm-2.17.so
7fd15d454000-7fd15d455000 rw-p 00101000 fd:01 3496                       /usr/lib64/libm-2.17.so
7fd15d455000-7fd15d5c6000 r-xp 00000000 fd:01 659485                     /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d5c6000-7fd15d7c6000 ---p 00171000 fd:01 659485                     /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d7c6000-7fd15d7d0000 r--p 00171000 fd:01 659485                     /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d7d0000-7fd15d7d2000 rw-p 0017b000 fd:01 659485                     /home/clx/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6
7fd15d7d2000-7fd15d7d6000 rw-p 00000000 00:00 0 
7fd15d7d6000-7fd15d7f8000 r-xp 00000000 fd:01 3479                       /usr/lib64/ld-2.17.so
7fd15d8e0000-7fd15d8e5000 rw-p 00000000 00:00 0 
7fd15d8ed000-7fd15d8ef000 rw-p 00000000 00:00 0 
7fd15d8ef000-7fd15d8f2000 r-xp 00000000 fd:01 16196                      /usr/lib64/libonion_security.so.1.0.19
7fd15d8f2000-7fd15d9f2000 ---p 00003000 fd:01 16196                      /usr/lib64/libonion_security.so.1.0.19
7fd15d9f2000-7fd15d9f3000 rw-p 00003000 fd:01 16196                      /usr/lib64/libonion_security.so.1.0.19
7fd15d9f3000-7fd15d9f7000 rw-p 00000000 00:00 0 
7fd15d9f7000-7fd15d9f8000 r--p 00021000 fd:01 3479                       /usr/lib64/ld-2.17.so
7fd15d9f8000-7fd15d9f9000 rw-p 00022000 fd:01 3479                       /usr/lib64/ld-2.17.so
7fd15d9f9000-7fd15d9fa000 rw-p 00000000 00:00 0 
7fff2b7be000-7fff2b7df000 rw-p 00000000 00:00 0                          [stack]
7fff2b7f0000-7fff2b7f2000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted

运行结果:内存错误
这是因为使用了野指针导致的。上面我们做过实现,Linux中g++编译器下vector采用二倍扩容机制。当我们插入三个数据后再次插入数据。顺序表进行扩容,发生深拷贝。开辟一块更大的空间将原空间数据拷贝过去,并释放原空间。此时pos指向的空间已经被释放。但pos还想访问并且删除此空间数据。产生非法访问。所以此时pos就是野指针

那么如何避免这两种问题呢
第一种:在定义完pos之后若对原数据进行挪动。重新使用find函数给pos赋值。保证新pos到pos被使用这段代码中没有将数据挪动。
第二种:合上面相近,pos定义后马上使用,不要在pos定义后进行增容扩容等。若有重新给pos赋值

为了更好的使用迭代器,STL中的函数实现包含了一些特性,帮助迭代器的使用和实现
测试三:erase的返回值

void vector_test3()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  v.push_back(5);
  v.push_back(6);  --插入六个数据
  vector<int>::iterator it  = v.begin();
  while (it != v.end())     --遍历数据
  {
    if (*it % 2 == 0)       --若是二的倍数删除该数据
    {
      v.erase(it);
    }
    it++;
  }
  for (auto e : v)        --打印数据
  {
    cout << e << " "; 
  }
  cout << endl;
}

运行程序

[clx@VM-20-6-centos test1_C++]$ make
g++ -c test.cpp -std=c++11
g++ -c vector.cpp -std=c++11
g++ -o test test.o vector.o -std=c++11
[clx@VM-20-6-centos test1_C++]$ ./test
Segmentation fault

显示分段故障
进行画图分析,为什么程序会崩
在这里插入图片描述
通过画图我们发现
第一:3 和 5并没有被判断语句判断。erase()函数将整个函数都向前移动了一格再加上it++跳过了这两个数值。假如这两个数为偶数,则无法被筛选出来。
第二:若最后一个数为偶数,那么最后一个数被删除v.end()将会移动到it位置,it++就跳过了v.end(),那么这个循环语句就无法结束

那么如何解决这个问题呢
在这里插入图片描述
我们发现erase函数的返回值是一个迭代器,这个迭代器会返回erase指定删除的数据的下一个数据位置,也就是erase(pos)中的pos位置。那么我们可以通过it接收pos,这样就可以避免it跳过数据

解决方案:

void vector_test4()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  v.push_back(5);
  v.push_back(6);
  vector<int>::iterator it  = v.begin();
  while (it != v.end())  --增加一个else语句,删除和加加只能分开执行
  {
    if (*it % 2 == 0)
    {
      it = v.erase(it);
    }
    else 
    {
      it++;
    }
  }
  for (auto e : v)
  {
    cout << e << " "; 
  }
  cout << endl;
}

在这里看上去不接受it的返回值也可以。但是在部分编译器中如VS,可能会对it++这一行为进行检查,若不接收it程序会崩溃这里就先不展开。还有些编译器中erase数据可能会缩容等。如果开辟了新的空间,it就成了野指针,最好使用erase之后要接收一下返回值。.

在这里插入图片描述

在vs中,即是v的size在最后只有3,但是it最后的值任然是6
最后再次强调接收it的值,不然在部分编译器下会报错

2.vector的模拟实现

2.1vector类成员总览

#pragma once 
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <assert.h>
using namespace std;


namespace clx
{
  template<class T>
  class vector
  {
    public:
      //iterator
      typedef T* iterator;
      typedef const T* const_iterator;
      iterator begin();
      iterator end();
      const_iterator begin() const;
      const_iterator end() const;


      vector();
      vector(const vector<T>& v); 
      vector<T>& operator=(vector<T> v);
      ~vector();


      //capacity
      size_t size() const;
      size_t capacity() const;
      void reserve(size_t n);
      void resize(size_t n, const T& val = T());
      bool empty() const;

      //modify
      void push_back(const T& val);
      void pop_back();
      void insert(iterator pos, const T& val);
      iterator erase(iterator pos);
      void my_swap(vector<T>& v);

     //see 
      T& operator[](size_t i);
      const T& operator[](size_t i) const;

    private:
      iterator _start;
      iterator _finish;
      iterator _endofstorage;

  };
  void vector_test1();
  void vector_test2();
  void vector_test3();
  template<class T>
  void vector_print(const vector<T>& v);
  
}

为了方便我们的vector可以存储各种各样的数据,我们使用类模板来定义vector
vector的成员变量是三个类型为T*的指针
_start 指向顺序表开头的元素
_finish 指向顺序表最后一个元素的后面一个位置
_endofstorage指向顺序表容量最大位置的后一个位置

2.2vector类迭代器

template<class T>
typename clx::vector<T>::iterator clx::vector<T>::begin()
{
  return _start;
}

template<class T>
typename clx::vector<T>::iterator clx::vector<T>::end()
{
  return _finish;
}

template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::begin() const
{
  return _start;
}

template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::end() const
{
  return _finish;
}

2.3vector默认成员函数

1.默认构造函数

template<class T>
clx::vector<T>::vector()
  :_start(nullptr)
  ,_finish(nullptr)
  ,_endofstorage(nullptr)
{}

2.拷贝构造函数

以前的写法

template<class T>
clx::vector<T>::vector(const vector<T>& v)
  :_start(nullptr)
  ,_finish(nullptr)
  ,_endofstorage(nullptr)
{
  if (v._start)
  {
    iterator _start = new T[v.capacity()]; //开辟和v相同大的空间
    //for ( const auto& e : v)
    //{
    //  push_back(e);
    //}
    for (size_t i = 0; i < size(); i++)   //逐个元素赋值
    {
      _start[i] = v._start[i];
    }
    _finish = _start + v.size();         
    _endofstorage = _start + v.capacity();
  }  
}

注意:这里可以使用范围for赋值,也可以使用for逐个赋值,但是不能使用memcpy来拷贝

现代写法

template<class T>
clx::vector<T>::vector(const vector<T>& v)
  :_start(nullptr)
  ,_finish(nullptr)
  ,_endofstorage(nullptr)
{
  reserve(v.capacity());
  for (auto e : v)
  {
    push_back(e);
  }
}

注意:拷贝构造还是需要初始化的,若不初始化,我们的tmp在析构时内部成员变量是随机值,析构时会产生非法访问内存

3.operator=

以前写法

template<class T>
  clx::vector<T>& clx::vector<T>::operator=(const vector<T>& v)
  {
    if (this != &v)
    {
      delete[] _start;
      _start = new T[v.capacity()];                                                                                                                                                   
      //for (const auto& e : v)
      //{
      //  push_back(e);
      //}
      for (size_t i = 0; i < size(); i++)
      {
        _start[i] = *(v._start + i);
      }
      _finish = _start + v.size();
      _endofstorage = _start + v.capacity();
    }
    return *this;
  }

现代写法

template<class T>
  clx::vector<T>& clx::vector<T>::operator=(const vector<T> v)
  {
    my_swap(*this, v);
    return *this;
  }

现代写法讲形参并未加引用,则v就是实参的一份拷贝,让*this和v交换,得到实参的拷贝,并且在v出函数时,析构v

4.析构函数

template<class T>                                                                                                                                                                       
clx::vector<T>::~vector()
{
  if(_start)
  {
    delete[] _start;
  }
  _start = _finish = _endofstorage = nullptr; //将空间释放,将指针制空
}

2.4vector容量相关函数

size()

template<class T>
size_t clx::vector<T>::size() const
{
  return _finish - _start;   //得到元素个数
}

获取元素个数

capacity()

template<class T>
size_t clx::vector<T>::capacity() const
{
  return _endofstorage - _start;
}

获取容量大小

template<class T>
void clx::vector<T>::reserve(size_t n) 
{
  if (n > capacity())           //若n大于容量
  {
    iterator tmp = new T[n];   //扩容
    size_t sz = size();        //获取原来元素的个数
    if (_start)
    {
      //memcpy(tmp, _start, sizeof(T) * sz); 
      for (size_t i = 0; i < sz; i++)   //将元素逐个拷贝
      {
        tmp[i] = _start[i];
      }
      delete[] _start;         //释放旧空间
    }
    _start = tmp;              //让指针指向正确位置
    _finish = tmp + sz;
    _endofstorage = tmp + n;
  }
}

改变容量
> 注意:创建sz变量来存储元素个数,是因为当_start = tmp时,_finish还未指向正确的位置,使用size()函数需要_start和_finish的位置,此时_start 是新空间的_start,而_finish是旧空间的_finish,两者毫无关系
memcpy局限性(在博客第四节会详细说明)

resize()


template<class T>
void clx::vector<T>::resize(size_t n, T val) 
{
  if (n < size())         //若比原元素数量还少,直接移动_finish
  {
    _finish = _start + n;
  }
  else 
  {
    if ( n > capacity())   //若比容量大,则扩容
    {
      reserve(n);
    }
    while (_finish != _endofstorage)  //将_finish 到_endofstorage左开右闭区间初始化成val
   {
      *_finish = val;
      ++_finish;
    }
  }
}

增容并且初始化

empty()

template<class T>
bool clx::vector<T>::empty() const 
{
  return size() == 0;
}

判断容器是否为空

2.5 vector类增删查改

push_back

template<class T>
void clx::vector<T>::push_back(const T& x)
{
  if (_finish == _endofstorage)  //如果元素数量等于容量,则进行扩容
  {
    size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
    reserve(newcapacity);
  }
  *_finish = x;                 //尾部插入x并且让元素个数加一
  _finish++;

}

insert()

template<class T>
void clx::vector<T>::insert(iterator pos, const T& val)
{
  size_t sz = pos - _start;    
  if (_finish == _endofstorage)
  {
    size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
    reserve(newcapacity);
    pos = _start + sz;
  }
  iterator end = _finish;
  while (end != pos)
  {
    *end = *(end - 1);
    end--;
  }
  *pos = val;
  _finish++;
}


在insert过程中,若发生了增容,那么vector的地址将会发生改变,形参pos就会成为野指针。这就是我们开始说的迭代器失效,为了避免我们可以在insert增容后重新给pos赋值,更新pos的含义。也可以想上面一样,找到pos和_start的相对距离,进行元素插入

pop_back()

template<class T>
void clx::vector<T>::pop_back()
{
  assert(size() > 0);
  _finish--;
}

erase()

template<class T> 
typename clx::vector<T>::iterator& clx::vector<T>::erase(iterator pos)
{
  iterator it = pos + 1;
  while (it != _finish)
  {
    *(it - 1) = *it;
    it++;
  }
  _finish--;
  return pos;
}

这两个函数本身都比较简单。但是在这个地方,因为我们的声明和定义是分离的,编译器并不知道我们的返回值clx::vector::iterator&是一个类型,他可能是一个静态成员函数,也可能是一个静态变量,所以我们要在前面加上一个typename来告诉编译器这是一个类型

2.6 swap 和 operator[ ]

swap()

template<class T>
void clx::vector<T>::my_swap(vector<T>& v)
{
  ::swap(_start, v._start);
  ::swap(_finish, v._finish);
  ::swap(_endofstorage, v._endofstorage);
}

operator[ ]

template<class T>
const T& clx::vector<T>::operator[](size_t i) const
{
  assert(i < size());
  return *(_start + i);
}

template<class T>
T& clx::vector<T>::operator[](size_t i) 
{
  assert(i < size());
  return *(_start + i);
}

2.7memcpy的局限性

在模拟实现的过程中,并没有使用memcpy进行拷贝。这是因为memcpy是通过字节长度进行的浅拷贝。若vector中所存数据为自定义类型,如string。那么memcpy进行拷贝的新空间上的数据还是原来的数据,也就是说前后两块空间的string为同一个string。此时我们释放就空间就会将string释放,现在的顺序表中的string类中的指针就变成了野指针。

3.vector类模拟源码

3.1Makefile

test:test.o vector.o
		g++ -o $@ $^ -std=c++11
test.o:test.cpp 
		g++ -c -g $< -std=c++11
vector.o:vector.cpp 
		g++ -c -g $< -std=c++11
.PHONY:clean
clean:
		rm -f ./*.o test

3.2vector.h

#pragma once 
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <assert.h>
using namespace std;


namespace clx
{
  template<class T>
  class vector
  {
    public:
      //iterator
      typedef T* iterator;
      typedef const T* const_iterator;
      iterator begin();
      iterator end();
      const_iterator begin() const;
      const_iterator end() const;


      vector();
      vector(const vector<T>& v); 
      vector<T>& operator=(vector<T> v);
      ~vector();


      //capacity
      size_t size() const;
      size_t capacity() const;
      void reserve(size_t n);
      void resize(size_t n, const T& val = T());
      bool empty() const;

      //modify
      void push_back(const T& val);
      void pop_back();
      void insert(iterator pos, const T& val);
      iterator erase(iterator pos);
      void my_swap(vector<T>& v);

     //see 
      T& operator[](size_t i);
      const T& operator[](size_t i) const;

    private:
      iterator _start;
      iterator _finish;
      iterator _endofstorage;

  };
  void vector_test1();
  void vector_test2();
  void vector_test3();
  template<class T>
  void vector_print(const vector<T>& v);
  
}

3.3vector.cpp

#include "vector.h"

template<class T>
typename clx::vector<T>::iterator clx::vector<T>::begin()
{
  return _start;
}

template<class T>
typename clx::vector<T>::iterator clx::vector<T>::end()
{
  return _finish;
}

template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::begin() const
{
  return _start;
}

template<class T>
typename clx::vector<T>::const_iterator clx::vector<T>::end() const
{
  return _finish;
}

template<class T>
void clx::vector<T>::my_swap(vector<T>& v)
{
  ::swap(_start, v._start);
  ::swap(_finish, v._finish);
  ::swap(_endofstorage, v._endofstorage);
}

template<class T>
clx::vector<T>::vector()
  :_start(nullptr)
  ,_finish(nullptr)
  ,_endofstorage(nullptr)
{}

template<class T>
clx::vector<T>::vector(const vector<T>& v)
  :_start(nullptr)
  ,_finish(nullptr)
  ,_endofstorage(nullptr)
{
  reserve(v.capacity());
  for (auto e : v)
  {
    push_back(e);
  }
}

template<class T>
clx::vector<T>& clx::vector<T>::operator=(vector<T> v)
{
  swap(v);
  return *this;
}

template<class T>
clx::vector<T>::~vector()
{
  if (_start)
  {
    delete[] _start;
  }
  _start = _finish = _endofstorage = nullptr;
}


template<class T>
size_t clx::vector<T>::size() const
{
  return _finish - _start;
}

template<class T>
size_t clx::vector<T>::capacity() const
{
  return _endofstorage - _start;
}

template<class T>
void clx::vector<T>::reserve(size_t n)
{
  if (n > capacity())
  {
    iterator tmp = new T[n];
      size_t sz = size();
      for (size_t i = 0; i < size(); i++)
      {
        tmp[i] = _start[i];
      }
      delete[] _start;
      _start = tmp;
      _finish = _start + sz;
      _endofstorage = _start + n;
  }
}

template<class T>
void clx::vector<T>::resize(size_t n, const T& val)
{
  if (n > capacity())
  {
    reserve(n);
  }
  if (n > size())
  {
    iterator it = _finish;
    while (_finish != _endofstorage)
    {
      *it = val;
      it++;
    }
  }
  if (n < size())
  {
    _finish = _start + n;
  }
}

template<class T>
void clx::vector_print(const vector<T>& v)
{
  typename vector<T>::const_iterator it = v.begin();
  while (it != v.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;
}

template<class T>
bool clx::vector<T>::empty() const 
{
  return size() == 0;
}

template<class T>
void clx::vector<T>::push_back(const T& val)
{
  if (_endofstorage == _finish )
  {
    size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
    reserve(newcapacity);
  }
  *_finish = val;
  _finish++;

}

template<class T>
void clx::vector<T>::pop_back()
{
  if (!empty())
  {
    _finish--;
  }
}

void clx::vector_test1()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  vector_print(v);
}


template<class T>
void clx::vector<T>::insert(iterator pos, const T& val)
{
  size_t sz = pos - _start;    
  if (_finish == _endofstorage)
  {
    size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
    reserve(newcapacity);
    pos = _start + sz;
  }
  iterator end = _finish;
  while (end != pos)
  {
    *end = *(end - 1);
    end--;
  }
  *pos = val;
  _finish++;
}

template<class T>
typename clx::vector<T>::iterator clx::vector<T>::erase(iterator pos)
{
  assert(!empty());
  iterator it = pos;
  while (pos < _finish - 1)
  {
    *it = *(it + 1);
  }
  _finish--;
  return pos;
}

void clx::vector_test2()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  vector_print(v);
  vector<int>::iterator pos = find(v.begin(), v.end(), 4);
  v.insert(pos, 5);
  vector_print(v);
  
}

template<class T>
const T& clx::vector<T>::operator[](size_t i) const
{
  assert(i < size());
  return *(_start + i);
}

template<class T>
T& clx::vector<T>::operator[](size_t i) 
{
  assert(i < size());
  return *(_start + i);
}

void clx::vector_test3()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  for (size_t i = 0; i < v.size() - 1; i++)
  {
    cout << v[i] << " ";
  }
  cout << endl;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白在进击

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值