当前位置: 首页 > news >正文

【C++】vector

【C++】vector

  • vector简介
  • vector类的常用接口
    • 构造
    • 迭代器
    • Capacity
    • 增删查改
  • vector类的模拟实现
    • 模板类
    • 成员变量
    • 搭一个框架
      • 无参构造
      • size && capacity
      • operator[]
      • push_back()
    • Capacity
      • size && capacity
      • empty
      • reserve
      • resize
    • 迭代器
      • begin()
      • end()
    • 增删查改
      • push_back
      • pop_back
      • insert
      • erase
      • swap
    • 完善构造函数
      • 迭代器区间构造
      • n value
      • 拷贝构造
      • 析构函数
    • 赋值拷贝 operator=
    • memcpy的浅拷贝问题
    • 迭代器失效
  • 代码

vector简介

  1. vector是一个表示可变大小数组的序列容器
  2. vector的底层存储和数组类似,采用连续的空间存储数据。这就意味着vector支持下标访问元素
  3. vector的空间大小是动态可变的,当新元素插入,空间不够时,它可以自动扩容
  4. vector会分配一些额外的空间来适应数据的增长,所以vector使用的空间比实际需要的空间要大
  5. 与其他动态序列容器相比,vector访问元素的效率较高,尾插尾删数据的效率也很不错;但是对于其他位置的修改,效率就很低了

vector类的常用接口

在使用容器时,查询文档是必不可少的:vector相关文档

下面列举一些常用的接口

构造

接口接口说明
vector()无参构造
vector(size_type n, const value_type& val = value_type ())构造并初始化 n 个 val
vector(const vector& v)拷贝构造
vector(InputIterator first, InputIterator last)使用迭代器进行初始化构造
void test1()
{// 无参vector<int> v1;// n valvector<int> v2(10, 1);// 拷贝构造vector<int> v3(v2);// 迭代器区间构造string s("abcd");vector<int> v4(s.begin(), s.end());for (auto& e : v1)cout << e << " ";cout << endl;for (auto& e : v2)cout << e << " ";cout << endl;for (auto& e : v3)cout << e << " ";cout << endl;for (auto& e : v4)cout << e << " ";cout << endl;
}

在这里插入图片描述

迭代器

接口接口说明
begin()获取第一个数据位置的 iterator
end()获取最后一个数据的下一个位置的 iterator
rbegin()获取最后一个数据位置的 reverse_iterator
rend()获取第一个数据前一个位置的 reverse_iterator

在这里插入图片描述

Capacity

接口接口说明
size()获取数据个数。返回值为容器中当前实际存储的元素个数。
capacity()获取容量大小。表示在不重新分配内存的情况下,容器可以容纳的元素数量。
empty()判断是否为空。如果容器中没有元素,则返回 true,否则返回 false。
resize()改变 vector 的 size。可以指定新的大小,如果新大小小于当前大小,容器会删除多余的元素;如果新大小大于当前大小,新添加的元素会进行值初始化(对于基本类型初始化为 0,对于类类型会调用默认构造函数)。
reserve()改变 vector 的 capacity。可以预先分配一定的内存空间,避免在添加元素时频繁进行内存重新分配操作。如果指定的容量小于当前容量,则此函数不做任何操作。

增删查改

接口接口说明
push_back()尾插
pop_back()尾删
find(算法模块实现)查找。查找特定元素,返回指向该元素的迭代器,如果未找到则返回尾迭代器。
insert在指定位置之前插入 val。可以插入单个元素或一段范围的元素。
erase删除 position 位置的数据。可以删除单个位置的元素,也可以删除一个范围内的元素。
swap交换两个 vector 的数据空间。快速交换两个向量的内容,而不需要逐个元素复制。
operator[]像数组一样访问。可以通过下标访问向量中的元素,但不进行边界检查,使用时需确保下标在合法范围内。

vector类的模拟实现

模板类

因为vector中可以存储不同类型的数据:内置类型、自定义类型,所以我们要将vector写成模板类

template <class T>
class vector
{};

成员变量

在vector中,许多功能都要依赖迭代器来实现,所以迭代器是很重要的

vector的迭代器底层是原生指针,所以vector的iterator可以使用 T* 来实现

template <class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;
};

在string的模拟实现中,我们用到了三个成员变量:

  • char* _str:字符数组的地址,用来存储字符串
  • size_t _size:用来表示字符串的实际长度
  • size_t _capacity:用来表示空间的总大小

模拟实现vector,我们用的三个变量和string的类似,只不过用的都是迭代器,而vector的迭代器底层就是原生指针,也就是我们的三个成员变量都是指针

  • iterator _start:指向第一个数据
  • iterator _finish:指向最后一个数据的下一个位置
  • iterator _endofstorage:指向空间的末尾

虽然换了一种形式,但是vector的成员变量和string的成员变量代表的含义是类似的

在这里插入图片描述

搭一个框架

先把无参构造、size()、capacity()、operator[]、push_back()这几个功能实现,搭一个能跑的框架

无参构造

不传参数构造一个对象,那么对象中就是空的,所以先不开空间,直接给三个成员变量赋值为空,等插入数据时再做开空间等操作

可以在声明时给成员变量缺省值,这里的缺省值是给初始化列表准备的,这样进入构造函数,走初始化列表时,就不用我们手动写了

template <class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;vector() {}
private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;
};

size && capacity

size_type size() const;
size_type capacity() const;
  • 两指针相减即可得到长度
  • size = _finish - _start
  • capacity = _endofstorage - _start
size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _endofstorage - _start;
}

operator[]

      reference operator[] (size_type n);
const_reference operator[] (size_type n) const;

返回指定下标的元素的引用

  • 可以加一个断言,防止越界操作
  • 因为底部是数组存储,所以直接返回 _start[n] 即可
T& operator[](size_t n)
{assert(n >= 0);assert(n < size());return _start[n];
}
const T& operator[](size_t n) const
{assert(n >= 0);assert(n < size());return _start[n];
}

push_back()

void push_back (const value_type& val);
  • 在插入数据之前,要检查空间是否已满,满了就要进行扩容。先在这里实现一个扩容,后面写了reserve再替换
  • 扩容时,如果原来的对象为空,就给 4 个初始空间;否则就 2 倍扩容。拷贝旧空间的数据到新空间时,可以使用 memcpy

在这里插入图片描述

  • 在尾部插入数据,更新 _finish
void push_back(const T& val)
{// 检查扩容if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();// 申请新空间,拷贝旧数据T* tmp = new T[newcapacity];memcpy(tmp, _start, sizeof(T) * size());delete[] _start;// 更新数据_start = tmp;_finish = tmp + size();_endofstorage = tmp + newcapacity;}// 插入数据*_finish = val;++_finish;
}

基本的架子搭建完成,先写一个打印vector的函数,测试一下插入数据

template <class T>
void PrintVector(const vector<T>& v)
{for (int i = 0; i < v.size(); i++)cout << v[i] << " ";cout << endl;
}

测试数据

void test1()
{vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);PrintVector(v1);
}

测试结果:

在这里插入图片描述

程序出错了,哪一步出错了呢?进入调试,发现问题出在 push_back 中,尾插数据时出现了空指针错误

在这里插入图片描述

虽然我们构造了一个空的vector对象,_finish应该是空指针,但是经过扩容后_finish就不应该是空了。那么问题就是出在扩容了,再次进入调试,看看申请空间后,三个成员变量是否更新成功

在这里插入图片描述

可以看到,执行完扩容的代码,_start和_endofstorage都不再是空指针了,唯独_finish还是空指针,而更新_finish的语句是这一句:

_finish = tmp + size();

tmp肯定是没有问题的,那么答案显而易见,问题出在size()

size_t size() const
{return _finish - _start;
}

原来是我们更新了_start的位置,但是 _finish 还留在原地,那么得到的size肯定是错误的

在这里插入图片描述

要解决这个问题,有两种办法

  1. 先更新_finish,再更新_start
// 更新数据
_finish = tmp + size();
_start = tmp;
_endofstorage = tmp + newcapacity;
  1. 一开始就记录旧的size,用旧size更新_finish,这里采用这个方法
// 更新数据
size_t old_size = size();
// ...
_start = tmp;
_finish = tmp + old_size;
_endofstorage = tmp + newcapacity;

测试:

在这里插入图片描述

既然完善了扩容,那接下来就写容量相关的接口吧

Capacity

size && capacity

size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _endofstorage - _start;
}

empty

bool empty() const;
  • 当 _start 和 _finish 指向同一个位置时,说明空间没有数据
bool empty() const
{return _start == _finish;
}

reserve

void reserve (size_t n);

上面我们写 push_back 时已经完成了 reserve,直接拿来用

void reserve(size_t n)
{if (n > capacity()){// 申请新空间,拷贝旧数据T* tmp = new T[n];memcpy(tmp, _start, sizeof(T) * size());// 更新数据size_t old_size = size();_start = tmp;_finish = tmp + old_size;_endofstorage = tmp + n;}
}

resize

void resize(size_t n, const T& val = T());
  • 当 n <= size() 时,需要将有效数据删除到 n 个。不需要真的删除数据,只需要更新 _finish 即可
  • 当 n > size() 时,需要填充数据为 val,这里的参数 val 有可能是自定义类型,所以缺省值是一个无参的匿名对象内置类型也有构造函数,调用无参构造时会对内置类型对象初始化,例如整型初始化为0,指针初始化为nullptr
void resize(size_t n, const T& val = T())
{if (n <= size()){// 删除数据_finish = _start + n;}else{// 填充数据reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}
}

在这里插入图片描述

迭代器

这里只实现begin()和end()以及它们的const版本

begin()

      iterator begin();
const_iterator begin() const;

获取第一个数据位置的迭代器,也就是获取第一个数据的指针

iterator begin()
{return _start;
}
const_iterator begin() const
{return _start;
}

end()

      iterator end();
const_iterator end() const;

获取最后一个数据下一个位置的迭代器

iterator end()
{return _finish;
}
const_iterator end() const
{return _finish;
}

测试:

在这里插入图片描述

增删查改

push_back

void push_back(const T& val)
{// 检查扩容if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);}// 插入数据*_finish = val;++_finish;
}

pop_back

void pop_back();
  • 尾删一个元素,只需_finish减一即可
  • 可以加一个断言,数据为空时不可以删除
void pop_back()
{assert(!empty());--_finish;
}

测试:

在这里插入图片描述

insert

iterator insert (iterator pos, const T& val);

在pos位置插入一个数据

  • 加一个断言,防止越界,insert也可以支持头插尾插
  • 在插入数据从之前,需要检查扩容
  • 然后就需要挪动数据了,将pos位置及其后面的数据向后挪动一个单位
  • 挪动完成后,更新数据即可
  • 返回新插入数据的位置的迭代器,也就是pos
iterator insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);// 检查扩容if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);}// 挪动数据iterator cur = end();while (cur > pos){*cur = *(cur - 1);--cur;}// 更新数据*pos = val;++_finish;return pos;
}

测试:

在这里插入图片描述

但是这里有个隐藏bug,多插入几个数据就会出错

在这里插入图片描述

这是为什么呢?这两次测试的区别就是:第二次测试插入的数据较多,发生了扩容。怎么又是扩容的问题??

我们之前遇到的问题是空间发生了更新,然而指向原空间的指针并未更新,这次的问题也是这样:

  • pos 指向原空间
  • 如果发生了扩容,pos位置未更新,后面又使用pos进行操作,大概率会发生错误

在这里插入图片描述

  • 所以如果需要扩容,先记录下pos相对_start的相对位置,扩容完成后更新pos
iterator insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);// 检查扩容if (_finish == _endofstorage){// pos相对位置size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);// 更新pospos = _start + len;}// 挪动数据iterator cur = end();while (cur > pos){*cur = *(cur - 1);--cur;}// 更新数据*pos = val;++_finish;return pos;
}

测试:

在这里插入图片描述

erase

iterator erase (iterator pos);

删除pos位置的数据

  • 断言,防止越界
  • 将 pos 后面的数据向前挪动一个单位
  • 返回被删除数据的后一个数据的迭代器,也就是pos
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);// 挪动数据iterator cur = pos + 1;while (cur != _finish){*(cur - 1) = *cur;++cur;}--_finish;return pos;
}

测试:

在这里插入图片描述

实现了 insert 和 erase,就可以复用代码来实现 push_back 和 pop_back

void push_back(const T& val)
{insert(end(), val);
}void pop_back()
{erase(end() - 1);
}

swap

两个vector对象的交换,如果调用算法库的swap,就是这样的:

template <class T> void swap ( T& a, T& b )
{T c(a); a=b; b=c;
}

拷贝构造一个临时对象c,又经历了两次赋值拷贝,一共经历了3次拷贝

如果T是比较复杂的自定义类型,3次拷贝消耗有点大,没有必要。我们只需要交换两个对象内部的指针,就可以达到交换数据的目的

在这里插入图片描述

void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}

测试:

在这里插入图片描述

完善构造函数

除了无参构造,我们还有几个构造函数没写

接口接口说明
vector()无参构造
vector(size_type n, const value_type& val = value_type ())构造并初始化 n 个 val
vector(const vector& v)拷贝构造
vector(InputIterator first, InputIterator last)使用迭代器进行初始化构造

迭代器区间构造

vector(InputIterator first, InputIterator last)

先来看最难的,其实也不难,就是之前没用过,有点陌生而已

我们可以使用另一个对象的迭代器区间进行构造,例如使用string对象的迭代器区间

在这里插入图片描述

  • 只需遍历取出区间中的元素,然后调用vector对象的尾插即可
  • 迭代器可能是string的迭代器,也可能是vector或者其他容器的,所以这里要使用函数模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}

测试:

在这里插入图片描述

n value

vector(size_t n, const T& val = T())

构造并初始化 n 个 val

  • 调用 n 次尾插即可
vector(size_t n, const T& val = T())
{for (int i = 0; i < n; i++)push_back(val);
}

测试:

在这里插入图片描述

程序编译错误,发现和 InputIterator 有关,为什么我们调用 n-val 的构造,会和迭代器区间构造有关系呢?——因为参数类型导致编译器调用的是函数模板

  • 创建对象时,传的参数是(10, 1),参数类型是 int int
  • n-val 构造函数的参数类型是 size_t T
  • 迭代器构造函数模板的参数则是 InputIterator InputIterator

在编译器看来,迭代器函数模板的参数可以推演成 int int,更加匹配,所以就调用函数模板

我们可以把第一个参数转换为 size_t 类型,再测试一下:

在这里插入图片描述

运行成功,此时我们传的参数类型是 size_t int,和 n-val 函数的参数类型更加匹配。但是我们不能每次都这样传参数吧,太麻烦了

解决办法就是重载一个参数类型是 int int 的 n-val 构造函数

vector(size_t n, const T& val = T())
{for (int i = 0; i < n; i++)push_back(val);
}
vector(int n, const T& val = T())
{for (int i = 0; i < n; i++)push_back(val);
}

测试:

在这里插入图片描述

拷贝构造

vector(const vector& v)

已存在的对象来构造一个新的对象

这里就直接使用现代写法了

  • 现代写法:不用我们手动开空间然后拷贝数据。直接将 v 的数据尾插到当前对象
vector(const vector<T>& v)
{reserve(v.capacity());for (auto& e : v)push_back(e);
}

测试:

在这里插入图片描述

析构函数

差点就把析构函数就给忘了

~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}

赋值拷贝 operator=

vector& operator= (const vector& x);

将已存在的对象赋值给另一个已存在对象

这里就不按照上面的函数头写了,使用现代写法

  • 传值传参,也就是拷贝构造一个临时对象v
  • 调用swap,将 *this 与 v 的数据进行交换
vector<T>& operator= (vector<T> v)
{swap(v);return *this;
}

测试:

在这里插入图片描述

memcpy的浅拷贝问题

我们在reserve的实现中,拷贝数据使用的是 memcpy 函数,它使用的是浅拷贝。如果vector中存的是自定义类型,且自定义类型会申请空间的话,那么析构时就会出现问题

例如使用string类型

在这里插入图片描述

目前来看代码可以正常运行,但是一旦扩容就会出现问题

在这里插入图片描述

以下是扩容之前的空间和扩容之后的空间,memcpy只是将旧空间中的数据拷贝到新空间,并不会把string指向的字符串也拷贝过来,这时旧空间和新空间的string对象指向同一块空间

在这里插入图片描述

扩容完毕后,我们需要调用 delete[] 释放旧空间,以下是delete[]的过程:

  • 在 free 旧空间之前,会调用string对象的析构函数,释放string对象申请的空间

在这里插入图片描述

  • 之后再调用 free 释放vector对象的空间

在这里插入图片描述

也就是说,当我们 delete[] 旧空间后,新空间中string指向的空间也已经被释放了

在这里插入图片描述

当调用 v1 的析构函数时,实际上string对象的空间已经被释放,会发生野指针问题

解决方法就是不用memcpy拷贝数据,直接遍历新开的空间和旧空间,将旧空间的数据赋值给新空间。如果数据是自定义类型,就会调用自定义类型的赋值拷贝,默认是深拷贝

void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();// 申请新空间,拷贝旧数据T* tmp = new T[n];// memcpy(tmp, _start, sizeof(T) * size());for (int i = 0; i < old_size; i++)tmp[i] = _start[i];delete[] _start;// 更新数据_start = tmp;_finish = tmp + old_size;_endofstorage = tmp + n;}
}

测试:

在这里插入图片描述

迭代器失效

假设有一个迭代器pos,当我们在pos位置插入或者删除数据后,pos就会失效

  1. 如果插入数据时发生了扩容,那么迭代器 pos 指向的空间就会被释放掉,此时 pos 就不可以再使用了

在这里插入图片描述

  1. 虽然删除数据不会发生空间的改变,但是 pos 位置的数据被删除,我们不能再通过 pos 得到原来的数据了,这也是迭代器失效

使用失效的迭代器是有风险的,有什么方法可以规避呢?

  1. 迭代器失效后,就不要再使用了
  2. 如果一定要使用失效的迭代器,那就更新迭代器后再使用

如何更新迭代器呢?

  • insert 插入数据成功后,会返回新插入的第一个数据的迭代器

在这里插入图片描述

  • erase 删除数据后,会返回被删除数据的后一个位置的迭代器

在这里插入图片描述
在插入或者删除数据后,及时接收 insert 和 erase 的返回值来更新迭代器

代码

#pragma once#include <iostream>
#include <assert.h>
#include <vector>
#include <string>
using namespace std;namespace ns1
{template <class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector() {}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}vector(size_t n, const T& val = T()){for (int i = 0; i < n; i++)push_back(val);}vector(int n, const T& val = T()){for (int i = 0; i < n; i++)push_back(val);}vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v)push_back(e);}vector<T>& operator= (vector<T> v){swap(v);return *this;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}bool empty() const{return _start == _finish;}void reserve(size_t n){if (n > capacity()){size_t old_size = size();// 申请新空间,拷贝旧数据T* tmp = new T[n];// memcpy(tmp, _start, sizeof(T) * size());for (int i = 0; i < old_size; i++)tmp[i] = _start[i];delete[] _start;// 更新数据_start = tmp;_finish = tmp + old_size;_endofstorage = tmp + n;}}void resize(size_t n, const T& val = T()){if (n <= size()){// 删除数据_finish = _start + n;}else{// 填充数据reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}}T& operator[](size_t n){assert(n >= 0);assert(n < size());return _start[n];}const T& operator[](size_t n) const{assert(n >= 0);assert(n < size());return _start[n];}iterator begin(){return _start;}const_iterator begin() const{return _start;}iterator end(){return _finish;}const_iterator end() const{return _finish;}void push_back(const T& val){ 检查扩容//if (_finish == _endofstorage)//{//        size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();//        reserve(newcapacity);//} 插入数据//*_finish = val;//++_finish;insert(end(), val);}void pop_back(){//assert(!empty());//--_finish;erase(end() - 1);}iterator insert(iterator pos, const T& val){assert(pos >= _start);assert(pos <= _finish);// 检查扩容if (_finish == _endofstorage){// pos相对位置size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);// 更新pospos = _start + len;}// 挪动数据iterator cur = end();while (cur > pos){*cur = *(cur - 1);--cur;}// 更新数据*pos = val;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);// 挪动数据iterator cur = pos + 1;while (cur != _finish){*(cur - 1) = *cur;++cur;}--_finish;return pos;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};template <class T>void PrintVector(const vector<T>& v){for (int i = 0; i < v.size(); i++)cout << v[i] << " ";cout << endl;}void test1(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);PrintVector(v1);}void test2(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);PrintVector(v1);cout << v1.size() << endl;cout << v1.capacity() << endl;// 填充v1.resize(20);PrintVector(v1);cout << v1.size() << endl;cout << v1.capacity() << endl;// 删除v1.resize(2);PrintVector(v1);cout << v1.size() << endl;cout << v1.capacity() << endl;}void test3(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);// 迭代器遍历vector<int>::iterator it1 = v1.begin();for (; it1 != v1.end(); it1++)cout << *it1 << " ";cout << endl;// 范围 forfor (auto& e : v1)cout << e << " ";cout << endl;}void test4(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);PrintVector(v1);v1.pop_back();PrintVector(v1);v1.pop_back();PrintVector(v1);v1.pop_back();PrintVector(v1);v1.pop_back();PrintVector(v1);v1.pop_back();PrintVector(v1);}void test5(){vector<int> v1;v1.push_back(1);PrintVector(v1);// 头插v1.insert(v1.begin(), 10);PrintVector(v1);// 尾插v1.insert(v1.end(), 30);PrintVector(v1);// 中间插v1.insert(v1.begin() + 1, 20);PrintVector(v1);// 尾插v1.insert(v1.end(), 100);PrintVector(v1);// 尾插v1.insert(v1.end(), 100);PrintVector(v1);// 尾插v1.insert(v1.end(), 100);PrintVector(v1);}void test6(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);v1.push_back(6);PrintVector(v1);// 头删v1.erase(v1.begin());PrintVector(v1);// 尾删v1.erase(v1.end()-1);PrintVector(v1);// 中间删v1.erase(v1.begin()+1);PrintVector(v1);}void test7(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);PrintVector(v1);vector<int> v2;v2.push_back(5);v2.push_back(6);v2.push_back(7);v2.push_back(8);PrintVector(v2);v1.swap(v2);PrintVector(v1);PrintVector(v2);}void test8(){string s1 = "abcd";vector<char> v1(s1.begin(), s1.end());PrintVector(v1);}void test9(){/*vector<int> v1(10, 1);vector<int> v2(v1);*/vector<int> v1(10, 1);vector<int> v2(10, 2);v1 = v2;PrintVector(v1);PrintVector(v2);}void test10(){vector<string> v1;v1.push_back("一");v1.push_back("二");v1.push_back("三");v1.push_back("四");v1.push_back("五");PrintVector(v1);}
}
);}void test7(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);PrintVector(v1);vector<int> v2;v2.push_back(5);v2.push_back(6);v2.push_back(7);v2.push_back(8);PrintVector(v2);v1.swap(v2);PrintVector(v1);PrintVector(v2);}void test8(){string s1 = "abcd";vector<char> v1(s1.begin(), s1.end());PrintVector(v1);}void test9(){/*vector<int> v1(10, 1);vector<int> v2(v1);*/vector<int> v1(10, 1);vector<int> v2(10, 2);v1 = v2;PrintVector(v1);PrintVector(v2);}void test10(){vector<string> v1;v1.push_back("一");v1.push_back("二");v1.push_back("三");v1.push_back("四");v1.push_back("五");PrintVector(v1);}
}

http://www.mrgr.cn/news/43401.html

相关文章:

  • 洛谷刷题 P1042 [NOIP2003 普及组] 乒乓球
  • Linux dlsym符号查找疑惑分析
  • SAP MM学习笔记 - 豆知识10 - OMSY 初期化会计期间,ABAP调用MMPV/MMRV来批量更新会计期间(TODO)
  • 懒洋洋浅谈--机器学习框架
  • 网络通信——OSPF和RIP的区别(总结)
  • 【漏洞复现】锐捷 RG-EW1200G 无线路由器 登录绕过
  • STL07——手写一个简单版本的unordered_set
  • Error while loading conda entry point: conda-libmamba-solver
  • C++ 语言特性29 - 协程介绍
  • 【Python】Dejavu:Python 音频指纹识别库详解
  • 【运动控制】关于GPIO的NPN型输入与NPN漏型输入
  • 算法工程师重生之第二十天(组合总和 组合总和II 分割回文串 )
  • 2024/10/5 数据结构打卡
  • 【MySQL】数据库基础
  • 几个卷积神经网络(CNN)可视化的网站
  • 快仓智能斩获过亿美元D轮融资,加速全球智能仓储与物流布局
  • 优化理论及应用精解【21】
  • 直立行走机器人技术概述
  • CSS选择器 快速入门
  • 分治算法(1)_颜色分类