讨论标准模板库std::vector的容量/大小及其内存增量

本文解析了std::vector的内存管理机制,包括其内存增量机制及其可能导致的引用失效问题。介绍了如何通过reserve、构造函数及resize等方法避免频繁的内存复制,保持引用的有效性。

首先解释一下容量/大小的区别:
  std::vector::capacity() : 指容器的能容纳多少个。
  std::vector::size() : 指容器当前已装多少个。
明白上面的意思思路就清晰多了。

 

这几天有同事用vector的时候遇到问题,说是当push_back以后,以前引用了vector的地址就不能访问。
造成以前引用的地址不能使用的原因估计大家都知道。
主要原因是vector有一个内存增量机制。


内增量机制是:为了满路容器的连续内存访问,当vector发现自己内存不够用,就需要重新申请内存,
然后复制旧的数据过去,删除旧的数据,重新指向新的数据地址。
当这个复制过程一结束的时候,以前引用了下标为0的vector内存地址就会不再是该数据地址了,
已被复制走的原有数据已删除成为野内存。
这时需要重去引用一次。

 

关于这个增量机制网上有多种说法,
  1说法:按原有尺寸的2倍增长,即:new_capacity = capacity_size * 2;
  2说法:按原有尺寸的50%增加,即:new_capacity = capacity_size / 2 + capacity_size;
  3说法:按数据尺寸倍数增长,即 sizeof(class) * 2;
  4说法:按字节来,即: (1024)/sizeof(class);


就此问题,我专门去看了看vector的原代码。得到的结果是:2说法正确。
这点可以从std::vector::_Insert_n(iterator, size_type, const _Ty&)的原代码中可以看出:

// 如果容量 < 大小 + 新数量增量
else if (_Capacity < size() + _Count)
{
  // 容量 = 容量 + 容量 / 2;
  _Capacity = max_size() - _Capacity / 2 < _Capacity ? 0 : _Capacity + _Capacity / 2; 
  // 容量 < 大小 + 新增数量
  if (_Capacity < size() + _Count)
    // 容量 = 大小 + 新增数量
    _Capacity = size() + _Count;
  // 申请新内存
  pointer _Newvec = this->_Alval.allocate(_Capacity);
  // 开始引用地址
  pointer _Ptr = _Newvec;
  // 复制与删除过程
  ......
}

 

那么如何解决我即想引用vector的地址又不需要经常去刷新该地址呢?
同时又解决掉vector的复制问题,也解决掉有些类的复制构造?
方法还是有的。
这里就有前面提到的容量/大小的因素了。
容量必然大于大小,是肯定的,即:capacity >= size;

 

方法1:
  创建std::vector容器,并调用std::vector::reserve(int n);
  告诉vector的直接增加n个数据并分配内存,
  这样的话push_back数量在n以内,引用的地址不会发生变化。vector内存也没有改变
  但如果事先告诉n的程序,这种方法是最好的。
  例:
    std::vector< int > vA;
    vA.reserve(10);
    vA.push_back(7);
    vA.push_back(6);
    vA.push_back(2);
    vA.push_back(9);
    int* pAddress = &vA[0];


方法2:
  通过构造函数告诉该容量容量
  各种带参数构造类似,这里不多举例
  例:
    std::vector< int > vA(10);
    int* pAddress = &vA[0];
    pAddress[0] = 7; // 等于 vA[0] = 7;
    pAddress[1] = 6; // 等于 vA[1] = 6;
    pAddress[2] = 2; // 等于 vA[2] = 2;
    pAddress[3] = 9; // 等于 vA[3] = 9;


方法3:
  与方法1方法一样。只是调用函数是:std::vector::resize(int n);

 

再说说resize与reserve的区别
resize:新的大小 < 现有大小,会删除多余的部分
        新的大小 > 现有大小,会在现有尺部插入新的数据,这时就有可能造成容量变化
        其它情况不操作。
reserve:当前容量 < 传入容量,会根据新的容量创建内存,并复制。
         当前容量 > 传入容量,不理踩
         其它情况不操作。

报错:guo@guo-Dell-G15-5520:~/g2o仿真/src/build$ make Consolidate compiler generated dependencies of target g2o_demo [ 50%] Building CXX object CMakeFiles/g2o_demo.dir/optimize.cc.o /home/guo/g2o仿真/src/optimize.cc: In member function ‘void TrajectoryOptimizer::optimizeTrajectory(std::vector<Eigen::Transform<double, 3, 1> >&)’: /home/guo/g2o仿真/src/optimize.cc:28:62: error: call of overloaded ‘make_unique<g2o::BlockSolverX>(std::remove_reference<std::unique_ptr<g2o::LinearSolverDense<Eigen::Matrix<double, -1, -1> >, std::default_delete<g2o::LinearSolverDense<Eigen::Matrix<double, -1, -1> > > >&>::type)’ is ambiguous 28 | make_unique<g2o::BlockSolverX>(move(linearSolver))); | ^ In file included from /usr/include/c++/9/memory:80, from /opt/ros/noetic/include/g2o/core/robust_kernel.h:30, from /opt/ros/noetic/include/g2o/core/base_binary_edge.h:34, from /home/guo/g2o仿真/src/optimize.cc:6: /usr/include/c++/9/bits/unique_ptr.h:856:5: note: candidate: ‘typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = g2o::BlockSolver<g2o::BlockSolverTraits<-1, -1> >; _Args = {std::unique_ptr<g2o::LinearSolverDense<Eigen::Matrix<double, -1, -1, 0, -1, -1> >, std::default_delete<g2o::LinearSolverDense<Eigen::Matrix<double, -1, -1, 0, -1, -1> > > >}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<g2o::BlockSolver<g2o::BlockSolverTraits<-1, -1> >, std::default_delete<g2o::BlockSolver<g2o::BlockSolverTraits<-1, -1> > > >]’ 856 | make_unique(_Args&&... __args) | ^~~~~~~~~~~ In file included from /opt/ros/noetic/include/g2o/core/base_binary_edge.h:36, from /home/guo/g2o仿真/src/optimize.cc:6: /opt/ros/noetic/include/g2o/stuff/misc.h:52:20: note: candidate:std::unique_ptr<_Tp> g2o::make_unique(ArgTs&& ...) [with T = g2o::BlockSolver<g2o::BlockSolverTraits<-1, -1> >; ArgTs = {std::unique_ptr<g2o::LinearSolverDense<Eigen::Matrix<double, -1, -1, 0, -1, -1> >, std::default_delete<g2o::LinearSolverDense<Eigen::Matrix<double, -1, -1, 0, -1, -1> > > >}]’ 52 | std::unique_ptr<T> make_unique(ArgTs&& ...args) | ^~~~~~~~~~~ make[2]: *** [CMakeFiles/g2o_demo.dir/build.make:76:CMakeFiles/g2o_demo.dir/optimize.cc.o] 错误 1 make[1]: *** [CMakeFiles/Makefile2:673:CMakeFiles/g2o_demo.dir/all] 错误 2 make: *** [Makefile:146:all] 错误 2
08-03
声明: #ifndef STATICDATAMANAGER_H #define STATICDATAMANAGER_H #include <vector> #include <mutex> #include <Eigen/Dense> #include <Eigen/Core> #include <memory> class StaticDataManager { public: // 静态成员变量 static Eigen::VectorXd intensity; static Eigen::MatrixXcd NFFT2; static std::vector<std::vector<int16_t>> data; // 私有构造函数,防止实例化 StaticDataManager() = delete; public: // 设置 intensity 数据 static void setIntensity(Eigen::VectorXd& newIntensity); // 获取 intensity 数据(返回副本) static Eigen::VectorXd getIntensity(); // 设置 NFFT2 数据 static void setNFFT2(Eigen::MatrixXcd& newNFFT2); // 获取 NFFT2 数据(返回副本) static Eigen::MatrixXcd getNFFT2(); // 设置 data 数据 static void setData(std::vector<std::vector<int16_t>>& newData); // 获取 data 数据(返回副本) static std::vector<std::vector<int16_t>> getData(); // 清空所有数据 static void clearAll(); }; #endif // STATICDATAMANAGER_H 实现: #include "StaticDataManager.h" // 初始化静态成员变量 Eigen::VectorXd StaticDataManager::intensity; Eigen::MatrixXcd StaticDataManager::NFFT2; std::vector<std::vector<int16_t>> StaticDataManager::data; // 设置 intensity 数据 void StaticDataManager::setIntensity(Eigen::VectorXd& newIntensity) { intensity = newIntensity; } // 获取 intensity 数据(返回副本) Eigen::VectorXd StaticDataManager::getIntensity() { return intensity; } // 设置 NFFT2 数据 void StaticDataManager::setNFFT2(Eigen::MatrixXcd& newNFFT2) { NFFT2 = newNFFT2; } // 获取 NFFT2 数据(返回副本) Eigen::MatrixXcd StaticDataManager::getNFFT2() { return NFFT2; } // 设置 data 数据 void StaticDataManager::setData(std::vector<std::vector<int16_t>>& newData) { data = newData; } // 获取 data 数据(返回副本) std::vector<std::vector<int16_t>> StaticDataManager::getData() { return data; } // 清空所有数据 void StaticDataManager::clearAll() { intensity = Eigen::VectorXd(); NFFT2 = Eigen::MatrixXcd(); data.clear(); }调用: StaticDataManager::setNFFT2(NFFT2); 报错:[build] gsy04w_measure.lib(ftpsimode.cpp.obj) : error LNK2019: 无法解析的外部符号 "public: static void __cdecl StaticDataManager::setNFFT2(class Eigen::Matrix<class std::complex<double>,-1,-1,0,-1,-1> &)" (?setNFFT2@StaticDataManager@@SAXAEAV?$Matrix@V?$complex@N@std@@$0?0$0?0$0A@$0?0$0?0@Eigen@@@Z),函数 "public: virtual void __cdecl FtpsiPhaseShift::Execute(void)" (?Execute@FtpsiPhaseShift@@UEAAXXZ) 中引用了该符号 帮忙分析解决下这个问题
最新发布
09-06
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值