ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

vector的使用与模拟实现

2026/8/2 19:59:04 拓冰建站 浏览量
vector的使用与模拟实现
目录
<vector>............................................................................................................................ 1
底层.......................................................................................................................... 1
与string的对比.................................................................................................. 1
使用.......................................................................................................................... 1
1.遍历................................................................................................................ 1
2.模拟二重数组.................................................................................................. 2
3.构造(C11).................................................................................................... 2
成员函数................................................................................................................... 3
modifier.............................................................................................................. 3
vector的模拟实现与坑介绍........................................................................................ 4
坑点一:size_t的遍历......................................................................................... 4
坑点二:模板重载匹配问题................................................................................ 5
坑点三:深层次的拷贝问题................................................................................ 6
迭代器失效处理.................................................................................................. 6
 

 

<vector>

底层

  底层使用的是数组来存储,实际上是一个顺序表(见数据结构与算法)

 

与string的对比

  底层都是数组,我们能不能用vector<char>来代替string呢?

    不能,与string不同的,vector没有连续不同元素输入的需求,也就没有重载find swap +=等一系列的运算符,无法对字符数组进行操作。

    我们可以发现vector没有find/swap这样的重载,本质是因为使用算法库中的就够用了

使用

1.遍历

  一样的三种遍历方法,[]/iterator/for(auto)

  需要注意的是,在储存自定义类型时,用范围for遍历一定是用引用,否则在堆上反复操作,代价太大。

eg:vector<string> a;a.push_back("张三");a.push_back("李四");for (auto& e : a)
{cout << e << " ";
}
cout << endl;
return 0;

 

2.模拟二重数组

 

(一)生成杨辉三角

eg:vector<vector<int>> generate(int numRows) {vector<vector<int>> vv;vv.resize(numRows,vector<int>());for(size_t i=0;i<numRows;i++){vv[i].resize(i+1,1);}for(size_t i=2;i<numRows;i++){for(size_t j=1;j<vv[i].size()-1;j++){vv[i][j]=vv[i-1][j-1]+vv[i-1][j];}}return vv;}

  

3.构造(C11)

    auto e = { 1,2,2,3,4,5,7,9 };

  //法一,显式初始化

    vector<int> a(e);

  //法二,隐式转化初始化

    vector<int> b({1,2,3});

  //法三,隐式转化拷贝复制

    vector<int> c={ 1,2,3 };

  //法四,特殊规则

    vector<int> d{ 1,2,3 };

 

成员函数

  这里选讲,由于底层和string一样是数组,其实库中成员函数的使用也差不多(详见string)

  下面主要进行一些补充

modifier

  insert

    注意,会导致传入的迭代器失效,不可二次使用该迭代器(本质缘由为空间拓展导致)

  erase

    注意,会导致传入的迭代器失效,不可二次使用该迭代器(本质缘由为VS编译器规定,本身不会产生野指针,除非操作不当)

  emplace

  这里主要介绍emplace的用法与改进,emplace作用和insert一样,而emplace_back则和push_back作用一致,唯一的不同是不难看出这个是C++11的新语法,其底层进行了复杂的优化,速度更快(后面介绍),同时默认构造传参语法也有一个新增。

eg:vector<A> v2;A aa1(1, 1);//push_backv2.push_back(aa1);v2.push_back(A(2,2));v2.push_back({3,3});cout << "**************************" << endl;//emplacev2.emplace_back(aa1);v2.emplace_back(A(2, 2));
//v2.emplace_back({ 3,3 });
// 传构造A的参数,效率较高,也是新增改动v2.emplace_back(3,3);

 

vector的模拟实现与坑介绍

  模拟实现gitee链接:(等待更新)

坑点一:size_t的遍历

  我们以反向历遍打印vector为例

print(const xx::vector& a)
for (size_t i=size()-1;i>0;i--)
{cout << a[i] << " " ;
}
cout << endl;

 

 

  这里i永远不会小于0,出现越界访问与死循环问题

  可以改进为

print(const xx::vector& a)
for (size_t i=size()-1; i!=0;i--)
{cout << a[i] << " " ;
}
cout << endl;

 

 

坑点二:模板重载匹配问题

  我们看到vector的拷贝构造,这里想用InputIterator来接收所有容器的不同的iterator,用模板适配,下面写的是用n个值进行初始化

 

template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last)
{push_back(*first);++first;}
}
vector(size_t n, T val = T())
{resize(n, val);
}
vector(int n, T val = T())
{resize(n, val);
}

 

 

  在测试用例:

  dr::vector<int> f(3,90);

  执行时,如果没有

  vector(int n, T val = T())

  {

         resize(n, val);

  }

  则会匹配到模板的部分(size_t版本会进行一次隐式转化,而模板更加精准匹配)。是不是这样就解决了其他相似的匹配问题?

  我们再看到测试用例

  dr::vector<size_t> f(3,90);

  现在是传入(int ,int),又匹配到模板去了。

 

  只要使用隐式类型转换进行传参就会产生类似的问题。在SGI STL3.0中,也就是解决了部分匹配问题(方法类似上面的,写几个确定的重载优先匹配,也存在上面dr::vector<size_t> f(3,90);的无法解决的问题),现有的语法无法解决,C++11之后推出了解决方法:SFINAE (Substitution Failure Is Not An Error),创造语法判定类型为你指定类型时才使用模板。

 

 

坑点三:深层次的拷贝问题

  看到vector的赋值重载/拷贝,我们要考虑到底层数组中存储的可能是自定义类型,使用memcopy的方式是错误的

eg:vector(const vector<T>& v): _start(new T[v.capacity()]),_finish(_start+v.size()),
_EndOfStorage(_start + v.capacity())
{//错误处理/**    memcpy(_start,v._start,v.size());*/reserve(v.capacity());for (size_t i = 0; _start + i != _finish; i++){_start[i] = v._start[i];}
}

 

迭代器失效处理

  在有迭代器传入的函数,一定记得返回一个新迭代器,用于函数外迭代器的更新(最好是不论底层指针是否还有效都设计更新,VS编译器检查严苛,之间报错,GCC略好)

eg:
void reserve(size_t n)
{if (n > size()){size_t oldsize = size();T* temp = new T[n];for (size_t i = 0; _start + i != _finish; i++){temp[i] = _start[i];}delete[] _start;_start = temp;_finish = _start + oldsize;_EndOfStorage = _start + n;}
}

 

 

持续优化ing……………………………………………………………………………………………………………………………………………………………………………………………………………………