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

C++ vector容器的解析和使用

C++的大部分接口是互通的 详解已经在string类中写过了 这里重复的接口属性就不详细写了

文章目录

  • 1.Member functions
    • constructor
    • std::vector::operator=
  • 2.Iterators
  • 3.Capacity
  • 4.Element access
  • 5. vector 增删查改
    • push_back&pop_back
    • insert&&find
    • erase
    • emplace_back
      • push_back和emplace_back的区别
      • 自定义类的遍历(含结构化绑定知识点)
  • 6. 例题板块
    • 例题1.只出现一次的数字(^ 的用法)
    • 例题2.杨辉三角(C和C++做法的比较)
  • 7. vector模拟实现和实现过程中的一些重难点
    • 7.1模拟实现完整代码
    • 7.2迭代器失效问题
      • 问题解析
      • 解决方案
    • 7.3 作为函数参数时 能否写成&的形式
    • 7.4 reserve的两个注意点
      • size()失效问题
      • 浅拷贝导致的内存管理问题
    • 7.5 Tips
      • 模版赋值
      • 缺省赋值
      • 多参数构造
  • 创作不易 求三连!

1.Member functions

C++提供的顺序表容器 在使用前建议先系统的学习过数据结构中的顺序表 详情请点

constructor

请添加图片描述
explicit 说明:防止隐式转换(vector v = 5 这是错误的 )
在这里插入图片描述

  1. 默认构造函数(无参构造)
    功能:创建一个空的 vector,不包含任何元素。
std::vector<int> v;  // 创建空的 int 类型 vector
std::cout << v.size();  // 输出 0(无元素)
  1. 填充构造函数(指定大小和初始值)
    功能:创建一个包含 n 个元素的 vector,每个元素的初始值为 val。
std::vector<int> v(3, 10);  // 创建包含3个元素的vector,每个元素为10
// 结果:v = [10, 10, 10]
  1. 范围构造函数(用迭代器区间初始化)
    功能:通过迭代器区间 [first, last) 中的元素初始化 vector,即复制该区间内的所有元素(包含 first 指向的元素,不包含 last 指向的元素)。
int arr[] = {1, 2, 3, 4};
std::vector<int> v(arr, arr + 3);  // 复制arr[0]、arr[1]、arr[2]
// 结果:v = [1, 2, 3]std::vector<int> v2(vec.begin(), vec.end());  // 复制另一个vector的所有元素
// 结果:v2 = [1, 2, 3]
  1. 拷贝构造函数(复制另一个 vector)
    功能:创建一个新的 vector,并复制另一个 vector x 中的所有元素(深拷贝,两个容器后续修改互不影响)。
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2(v1);  // 拷贝v1的元素
// 结果:v2=[1, 2, 3]

然后C++11中还引入了initializer_list(使用需要包含initializer_list的头文件)

#include<iostream>
#include<vector>
#include<initializer_list> 
int main() {// 假设 il 是一个整数类型的初始化列表auto il = {1, 2, 3, 4, 5};  // 推断为 initializer_list<int>// 输出 il 的类型名(不同编译器可能显示不同,通常为 "initializer_list<int>" 相关形式)cout << typeid(il).name()<<endl;// 遍历初始化列表中的元素并输出for (auto e : il) {cout << e << " ";}cout<<endl;  // 输出:1 2 3 4 5// 用初始化列表直接初始化 vector(C++11 及以上支持)vector<int> v1({10, 20, 30});  // v1 包含元素 10, 20, 30return 0;
}
vector (initializer_list<value_type> il,
const allocator_type& alloc = allocator_type());

vector 的初始化:
vector<int> v1({10,20,30})或者vector<int> v1=({10,20,30}) (构造传参)是 C++11 引入的列表初始化方式,等价于更简洁的 vector<int> v1{10,20,30} 或者vector<int> v1={10,20,30}(走隐式类型转换 构造+拷贝构造 直接优化为构造),直接构造包含指定元素的向量。
注意:initializer_list 中的元素是常量,无法修改(如 vector[0] = 5 错误)。
而且 他的遍历是要通过迭代器的方式 但是放在别的容器中还可以让他像数组一样!
在这里插入图片描述

initializer_list初始化的本质相当于 在范围for中 一直用push_back()插入数据。
在这里插入图片描述

std::vector::operator=

copy (1) vector& operator= (const vector& x);

赋值重载 将新内容分配给容器,替换其当前内容,并相应地修改其大小。
功能说明

  • 作用:把 vector x 中的所有元素复制到当前 vector 中。
  • 特性:这是一个深拷贝操作,赋值后两个 vector 各自拥有独立的元素,修改其中一个不会影响另一个。
  • 行为:赋值前会先清空当前 vector 中的原有元素,再复制 x 的元素。
// vector assignment
#include <iostream>
#include <vector>int main ()
{vector<int> a (3,0);vector<int> b (5,0);b = a;a = vector<int>();cout << "Size of a: " << int(a.size()) << '\n';cout << "Size of b: " << int(b.size()) << '\n';return 0;
}

结果:Size of a: 0 ;Size of b: 3

2.Iterators

迭代器 没什么好说的 但是有一说一 vevtor容器是不支持直接像数组一样遍历 必须使用迭代器请添加图片描述
其接口特性和string 基本一致 详情请转跳

在这里插入图片描述

在这里插入图片描述

3.Capacity

在这里插入图片描述
在这里插入图片描述

vector的容器部分 其效果基本也与string 相似 本质上是并没有太大的区别
详情请转跳

4.Element access

在这里插入图片描述
vector的容器部分 其效果基本也与string 相似 本质上是并没有太大的区别
详情请转跳

5. vector 增删查改

这里面会产生一些不同于string容器的变化 我这里会挑一些重点讲 请添加图片描述

push_back&pop_back

void push_back (const value_type& val);
void push_back (value_type&& val);
vector<int> v1;v1.reserve(100);//扩容到100 减少多次扩容size_t old=v1.capacity();cout<<old<<endl;for (size_t i=0; i<100; i++)//尾插{v1.push_back(i);if (v1.capacity()!=old){cout<<v1.capacity()<<endl;old=v1.capacity();}}

请添加图片描述

void pop_back();
Delete last element

vector::pop_back() 只会减少 vector 的元素数量(size),不会改变其容量(capacity)。
移除向量中的最后一个元素,实际上会使容器大小减少一个。
这会销毁被移除的元素。 这里就不演示了

insert&&find

  1. 插入单个元素
iterator insert (iterator position, const value_type& val);

string不同 vector没有办法使用pos 而是要使用迭代器
返回值:指向新插入元素的迭代器(方便后续操作)

vector<int> v = {1, 3, 4};
// 在索引1位置插入元素2(通过迭代器 v.begin() + 1 指定位置)
v.insert(v.begin() + 1, 2);
// 结果:v = [1, 2, 3, 4],it 指向新插入的 2
  1. 插入 n 个重复元素
void insert (iterator position, size_type n, const value_type& val);

返回值:void

std::vector<int> v = {1, 4};
// 在开头插入 2 个元素 0
v.insert(v.begin(), 2, 0);
// 结果:v = [0, 0, 1, 4]
  1. 插入迭代器范围元素
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);

返回值:void

std::vector<int> v1 = {1, 5};
std::vector<int> v2 = {2, 3, 4};
// 在 v1 的索引1位置插入 v2 的所有元素
v1.insert(v1.begin() + 1, v2.begin(), v2.end());
// 结果:v1 = [1, 2, 3, 4, 5]

虽然vector容器里面并没有现成的find 但是在std中是有的

template <class InputIterator, class T>InputIterator find (InputIterator first, InputIterator last, const T& val);

返回一个迭代器,指向范围 [first, last) 中第一个与 val 比较相等的元素。如果未找到这样的元素,则函数返回 last。

auto it=find(v1.begin(), v1.end(), 5);//返回的也是迭代器if (it!=v1.end()){v1.insert(it, 50);//在数字5的位置插入50}

erase

iterator erase (iterator position);
iterator erase (iterator first, iterator last);
  1. 删除单个元素
vector<int> v = {1, 2, 3, 4};
// 删除索引1处的元素(值为2)
auto it = v.erase(v.begin() + 1);
// 结果:v = [1, 3, 4],it 指向元素3(被删除的下一个元素)
  1. 删除范围内的元素
vector<int> v = {1, 2, 3, 4, 5};
// 删除索引1到3(不包含3)的元素(即2和3)
auto it = v.erase(v.begin() + 1, v.begin() + 3);
// 结果:v = [1, 4, 5],it 指向元素4

emplace_back

这个接口因为目前知识深度的原因 没发讲全 但是也是可以讲一部分

vector<int> v1{10,20,30};//让其像数组一样cout<<v1[1]<<endl;//输出 20v1.push_back(1);v1.emplace_back(1);//这么用可以理解成两者没有区别

push_back和emplace_back的区别

这个是我们自定义的结构体

struct A
{A(int a1,int a2):_a1(a1),_a2(a2){cout<<"A(int a1,int a2)"<<endl;}A(const A& aa):_a1(aa._a1),_a2(aa._a2){cout<<"A(const A& aa)"<<endl;}int _a1;int _a2;
};
    vector<A> v2;//push_back的三种方法//1.有名对象A aa1(1,1);v2.push_back(aa1);//2.匿名对象v2.push_back(A(2,2));//3.隐式类型转换v2.push_back({3,3});v2.emplace_back(aa1);v2.emplace_back(A(2,2));//v2.emplace_back({3,3)}; 不能这么用//可以这么用//传构造A的参数,效率比较高v2.emplace_back(3,3);//直接构造

前两种可以理解二者没有区别 最后一种是采用直接的构造效率更高

自定义类的遍历(含结构化绑定知识点)

    //A*vector<A>::iterator it2=v2.begin();while (it2!=v2.end()){// cout<<*it2<<" ";这么写是错误的 因为没有重载流插入cout<<(*it2)._a1<<":"<<(*it2)._a2<<endl;cout<<it2->_a1<<":"<<it2->_a2<<endl;//这么写也可以++it2;}cout<<endl;//当然肯定是范围for更香一点 c++11for(auto& aa:v2){cout<<aa._a1<<" "<<aa._a2<<endl;}//c++17 新出的语法糖 当容器是结构的时候就可以这么用auto[x,y]=aa1;//取aa1的成员依次进行赋值for(auto[x,y] :v2)//结构化绑定{cout<<x<<":"<<y<<endl;}for(auto&[x,y] :v2)//不想有拷贝构造函数{cout<<x<<":"<<y<<endl;}

请添加图片描述

6. 例题板块

例题1.只出现一次的数字(^ 的用法)

请添加图片描述

class Solution {
public:int singleNumber(vector<int>& nums) {int x = 0;  // 初始化为0for (auto e : nums) {  // 遍历数组中的每个元素x ^= e;  // 累积异或运算}return x;  // 最终结果就是只出现一次的数字}
};

核心原理:异或运算的特性

  • 自反性:a ^ a = 0(任何数与自身异或结果为 0)
  • 恒等性:a ^ 0 = a(任何数与 0 异或结果为其本身)
  • 交换律和结合律:a ^ b ^ c = a ^ c ^ b(运算顺序不影响结果)

假设数组为 [2, 3, 2, 4, 4]

x = 0 ^ 2 → 2
x = 2 ^ 3 → 1(二进制 10 ^ 11 = 01)
x = 1 ^ 2 → 3(二进制 01 ^ 10 = 11)
x = 3 ^ 4 → 7(二进制 11 ^ 100 = 111)
x = 7 ^ 4 → 3(二进制 111 ^ 100 = 011)
最后结果就是 3

异或运算的自反性(a ^ a = 0)和恒等性(a ^ 0 = a)在此过程中起关键作用:
数组中出现两次的数字(如 2、4),经过两次异或后会相互抵消(结果为 0)。
最终剩下的数字就是只出现一次的数字(如 3),因为它只被异或了一次。

例题2.杨辉三角(C和C++做法的比较)

在这里插入图片描述

如果用C语言来写的话 你需要动态开辟二纬数组 释放的时候需要讲空间进行分别进行释放
请添加图片描述
如果你想用c写,你需要返回两个你不怎么认识的指针给测试者。
其中 *returnSize是让调用者知道有多少行,然后 *returnColumnSizes是让调用者知道为每一行分配的列数。

int** generate(int numRows, int* returnSize, int** returnColumnSizes) {int** aa =(int*) malloc(numRows * sizeof(int*));//为指针数组分配内存,定义行数//对程序结构没影响,但如果缺少则无法与出题者交互,进而不能通过测试*returnSize = numRows;//让调用者知道有多少行*returnColumnSizes = (int*)malloc(numRows * sizeof(int));//为每一行分配列数做准备【x,x,x,x,x...】for (int i = 0; i < numRows; i++) {triangle[i] =(int*) malloc((i+ 1) * sizeof(int));//为特定的行分配内存,定义了该行的具体元素数量(*returnColumnSizes)[i] = i + 1; // 为每一行分配列数【1,2,3,4,5...】,让调用者知道// 每行的第一个和最后一个元素为 1aa[i][0] = 1;aa[i][i] = 1;// 计算当前行中间的元素for (int j = 1; j < i; j++) {aa[i][j] = aa[i - 1][j - 1] + aa[i - 1][j];}}return aa; // 返回生成的杨辉三角}

让我们看看C++是怎么做的

class Solution {
public: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<vv.size();i++){for(size_t j=1;j<vv[i].size()-1;j++){vv[i][j];//本质逻辑 //vv.operator[](i).operator[](j)vv[i][j]=vv[i-1][j]+vv[i-1][j-1];}}return vv;}
};
vv.resize(numRows, vector<int>())

这个操作将 vv 调整为有 numRows 行,每行是一个空的 vector。注意,vector() 是一个空的 int 类型的向量,表示每一行的初始状态为空
请添加图片描述
里面的vector<int>相当于 一个int*的数组 同时伴随容器的sizecapacity,而外层的vector则是一个包含着一个vector<int>*数组的一个类。

  • 外部 vector (vv): 存储的是指向内部 vector 的指针。
  • 内部 vector (vector): 每个内部的 vector 实际上是一个独立的动态数组。

而这么做的目的也就是为开辟一个动态的二维数组。这也就是STL容器所带来的简便性!

7. vector模拟实现和实现过程中的一些重难点

_start:指向内存空间的起始位置,即第一个元素的地址。
_finish:指向当前已存储元素的下一个位置,即末尾元素的地址 + 1。
_endofstorage:指向已开辟内存空间的末尾位置(已开辟内存的最后一个字节的下一个位置)。

7.1模拟实现完整代码

#include<iostream>
#include <cassert>
using namespace std;//模版 申明和定义不能分离定义到两个文件.h .cpp
namespace fcy
{
template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}
//    vector():
//    _start(nullptr)
//    ,_finish(nullptr)
//    ,_endofstorage(nullptr)
//    {}//因为有了缺省值所以还可以
//    vector()
//    {}//c++11强制编译器生成默认构造vector()=default;//多参数拷贝构造vector(initializer_list<T> il){reserve(il.size());for(auto& e:il){push_back(e);}}//迭代器区间初始化 但这么写只能传vector 的iterator
//    vector(iterator start,iterator end)
//    {
//        while (start!=end)
//        {
//            push_back(*start);
//            ++start;
//        }
//    }template<class it>//函数模版//只要类型匹配 我可以用任意类型迭代器来进行初始化vector(it first,it last){while (first!=last){push_back(*first);first++;}}//非法间接寻址
//    vector(size_t n,T val=T())
//    {
//        resize(n,val);
//    }//改成int就没问题吗? 这只能解决一种情况 所以咱们就多搞两个vector(int n,T val=T()){resize(n,val);}vector(size_t n,T val=T()){resize(n,val);}//v2(v1) 拷贝构造 写法1 这种写法必须要初始化 不然reserve鬼知道会发生什么 迭代器都是随机值 当然也可以用缺省值进行初始化vector(const vector<T>& v)// _start(nullptr)//,_finish(nullptr)//,_endofstorage(nullptr){reserve(v.capacity());for(auto& e:v){push_back(e);}}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}//v1=v7vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if(_start){delete[] _start;}_start=_finish=_endofstorage=nullptr;}//string 由_str _size _capacity 组成//扩容void reserve(size_t n){if(n>capacity()){size_t old_size=size();//修正1T* tmp=new T[n];//拷贝旧空间数据到新空间if (_start){// memcpy(tmp,_start,sizeof(T)*size());//string中的指针也会原样赋值 会导致str*指向同一空间//然后空间释放时候导致tmp所指向的也一样释放了//导致了浅拷贝//所以建议用赋值来解决for(size_t i=0;i<old_size;i++){tmp[i]=_start[i];}//int这样的类型,赋值可以完成浅拷贝//string这样的类型,赋值重载完成深拷贝delete[] _start;}_start=tmp;//_finish=_start+size();//此时的size()是有问题的 因为此时//size=_finish(原来的)-_start=0(因为初始化时候为0)-_start//所以要提前更新 参考修正1_finish=_start+old_size;_endofstorage=_start+n;}}void resize(size_t n,T val =T())//本质就是调用默认构造{if(n>size()){reserve(n);while (_finish!=_start+n){*_finish=val;++_finish;}}else{_finish=_start+n;}}T& operator[](size_t i){assert(i<size());return _start[i];}const T& operator[](size_t i)const{assert(i<size());return _start[i];}void clear(){_finish=_start;}size_t size() const{return _finish-_start;}size_t capacity() const{return  _endofstorage-_start;}//尾插void push_back(const T& x){if (_finish==_endofstorage){reserve(capacity()==0?4:capacity()*2);}*_finish=x;_finish++;}//尾删void pop_back(){assert(!empty());--_finish;}bool empty() const{return  _start==_finish;}iterator insert(iterator pos,const T& x)//不能用& 因为假设传的begin()调用函数iterator begin() 或者说 是表达式的形式 返回的是零时对象 具有常性{assert(pos>=_start);assert(pos<=_finish);if(_finish==_endofstorage){size_t len=pos-_start;reserve(capacity()==0?4:capacity()*2);//扩容会引起迭代器失效//更新pospos=_start+len;//指向新内存中插入的数值的位置,是有效的迭代器}iterator end=_finish-1;while(end>=pos){*(end+1)=*end;--end;}*pos=x;_finish++;return pos;}iterator erase(iterator pos){assert(pos>=_start);assert(pos<_finish);//挪动数据iterator it=pos+1;while (it!=_finish){*(it-1)=*it;it++;}_finish--;return pos;//返回指向删除数据的下一个位置//比如删除位置为2 结束后指向位置3}private:iterator _start=nullptr;iterator _finish=nullptr;//最后一个数据的下一个iterator _endofstorage=nullptr;//已开辟内存的最后一个字节的下一个位置
};
}

7.2迭代器失效问题

问题解析

首先我们需要先了解迭代器是什么?
迭代器的本质:指向内存地址的指针
vector的迭代器本质是原生指针(或行为类似指针的对象),直接指向元素在内存中的地址。例如:
若pos迭代器指向内存地址0x1000(对应元素A),那么*pos就是访问0x1000地址上的数据。
为什么迭代器会失效呢?
迭代器的核心作用是 “指向特定元素”。删除后,pos原本指向的元素A已被移除,0x1000地址上的元素变成了B,pos不再指向 “原来的元素”,语义上已失效。

//insert 部分iterator insert(iterator pos,const T& x){if(_finish==_endofstorage){size_t len=pos-_start;reserve(capacity()==0?4:capacity()*2);//扩容会引起迭代器失效pos=_start+len;}

这是扩容的部分 代码 引起迭代器失效的罪魁祸首就是扩容操作里的

            T* tmp=new T[n];//拷贝旧空间数据到新空间if (_start){for(size_t i=0;i<old_size;i++){tmp[i]=_start[i];}delete[] _start;}_start=tmp;

这里delete空间释放是导致其出现迭代器失效的罪魁祸首。

//erase部分iterator it=pos+1;while (it!=_finish){*(it-1)=*it;it++;}_finish--;

而这里导致迭代器失效的主要原因就是因为 删除元素后 替他元素的前移 虽然指向的地址不变 但是指向的内容发生改变 所以迭代器失效哦。

解决方案

首先在std库中我们参考源代码
erase 提供了一个可以返回的 新的有效迭代器,指向 “被删除元素的下一个元素”,供用户更新迭代器,避免使用失效的迭代器。

假设我们有一个 vector 存储元素 [10, 20, 30, 40],内存布局如下(地址为示例):
请添加图片描述

  1. 删除 pos 指向的元素(30)
    执行 erase(pos) 时,vector 会将 pos 之后的元素(40)往前移动,覆盖 pos 位置的元素:
    40 从 0x100C 移到 0x1008(原 30 的位置)。

最终元素变为 [10, 20, 40],_finish 指向 0x100C(尾后位置)。

  1. 原 pos 迭代器指向的内容
    原 pos 迭代器本质是指针,仍指向 0x1008 这个内存地址,但该地址上的元素已从 30 变成了 40。
    此时:若解引用原 pos(*pos),会得到 40(而非被删除的 30)。

原 pos 迭代器在语义上已失效:它本应指向 “被删除的 30”,但现在指向的是 “原 30 后面的 40”,与预期逻辑不符。

  1. erase 返回值指向的内容
    erase 会返回 被删除元素的下一个元素的迭代器,即原 40 的位置(移动后 40 在 0x1008,下一个元素是尾后位置 0x100C)。

因此返回的迭代器指向 0x100C(新的尾后位置),是有效的。


insert的解决方案 则是如果扩容 通过记录和更新pos的方式来阻止迭代器失效

        if(_finish==_endofstorage){size_t len=pos-_start;reserve(capacity()==0?4:capacity()*2);//扩容会引起迭代器失效//更新pospos=_start+len;//指向新内存中插入的数值的位置,是有效的迭代器}

这里提一嘴 insert的返回值 始终指向新插入的元素的位置,是唯一有效的迭代器,需用它更新外部迭代器(比如你在 1 3 4 数组的 1后插入个2 那迭代器返回的就是2 的位置和元素)

7.3 作为函数参数时 能否写成&的形式

iterator insert(iterator& pos,const T& x) 能否这么写?
答案是 NO!

这里举个例子

    fcy::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.insert(v.begin(), 0);

当我们想在开头位置传入0时候 如果以引用的方式写 会发生报错。
为什么?

    iterator begin(){return _start;}

当调用 insert(v.begin(), 0) 时,begin() 返回的是一个临时迭代器对象(右值 具有常性质)。C++ 规定 “非 const 左值引用(iterator&)不能绑定到右值”,此时编译会直接报错。
而这也就是我们常说的权限放大问题!
首先临时对象是具有常性的 具体为何这里会是临时对象 可以参考我以前写过的文章

7.4 reserve的两个注意点

大部分人一开始写这个功能都会这么写

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

乍一看没问题 其实漏洞百出

size()失效问题

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

问题就出现在了这三行代码中

_start=tmp;
_finish=_start+size();
_endofstorage=_start+n;

_finish = _start + size()中,size()的计算是_finish - _start,但此时_start已被更新为新空间的指针(tmp),旧的_finish指针已经失效,导致size()计算结果错误。

解决方案
记录原来的size()即可

size_t old_size=size();//提前记录原来的size_start=tmp;_finish=_start+old_size;_endofstorage=_start+n;

浅拷贝导致的内存管理问题

问题出现在这里

memcpy(tmp,_start,sizeof(T)*size());
delete[] _start;

memcpy是字节级别的浅拷贝,仅适用于内置类型(如int、double)。对于自定义类型(如std::string、包含指针成员的类),memcpy会直接拷贝指针值,导致新空间和旧空间的元素共用同一块内存,析构时会触发双重释放(delete[] _start释放旧空间后,新空间的元素指针仍指向已释放的内存,后续析构新空间时再次释放)。
解决方案
用循环赋值的方法

        if (_start){// memcpy(tmp,_start,sizeof(T)*size());//string中的指针也会原样赋值 会导致str*指向同一空间//然后空间释放时候导致tmp所指向的也一样释放了//导致了浅拷贝//所以建议用赋值来解决for(size_t i=0;i<old_size;i++){tmp[i]=_start[i];}//int这样的类型,赋值可以完成浅拷贝//string这样的类型,赋值重载完成深拷贝delete[] _start;}

7.5 Tips

模版赋值

template<class T>
vector(int n,T val=T())

模版赋值可以采用匿名对象的方式进行赋值

缺省赋值

    iterator _start=nullptr;iterator _finish=nullptr;//最后一个数据的下一个iterator _endofstorage=nullptr;//已开辟内存的最后一个字节的下一个位置

缺省值是要走初始化列表的 这么写可以让初始化更方便

//    vector():
//    _start(nullptr)
//    ,_finish(nullptr)
//    ,_endofstorage(nullptr)
//    {}//因为有了缺省值所以还可以vector(){}//c++11强制编译器生成默认构造vector()=default;

多参数构造

才用初始化列表的方式

vector(initializer_list<T> il){reserve(il.size());for(auto& e:il){push_back(e);}}

创作不易 求三连!

http://www.dtcms.com/a/499916.html

相关文章:

  • STM32G474单片机开发入门(十四)SPI总线详解及NRF2401模块实战
  • 接口测试的测试用例应该怎么写?
  • java.lang 包详解
  • GTA5浏览器网站建设中建设银行网站定酒店
  • C++编程学习(第38天)
  • 许昌做网站公司哪家专业找装修公司的网站
  • 问题“A fatal error occurred: Failed to connect to ESP32”分析解决
  • 基数排序基础透彻理解
  • 批处理重命名遇到的几个问题
  • 网站风格的设计微信小程序开发费用一览表
  • 门户网站开发框架应用网站
  • 做网站如何调字体格式衡阳做网站ss0734
  • 云南省城乡和住房建设厅网站大连seo建站公司
  • 微服务降本增效措施
  • [优选算法专题四.前缀和——NO.27 寻找数组的中心下标]
  • 多模块exe文件和ini文件文件之间是如何耦合的
  • 【MySQL】数据库的相关操作
  • 房地产网站源码Wordpress页面无侧边栏
  • 营销神器官方网站竞价单页模板
  • Mysql初阶第十一讲:Mysql视图特性与用户管理
  • 临沂h5建站网站建设企业网银e路通
  • gpiozero 树莓派(Raspberry Pi)官方推荐的 Python GPIO 控制库
  • 如何快速搭建个人网站wordpress 发通知
  • 深圳网站建设公司收费手机排行榜软件
  • ModuleNotFoundError: No module named ‘google.protobuf‘
  • 江苏企业网站排名优化wordpress文章序号排列
  • 网站空间使用方法wordpress php版本号
  • 网站建设需要学什么证苏州保洁公司诗雨
  • 网站用户推广哈尔滨快速建站服务
  • WinForm自定义组件双击事件