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

C++ 容器常见问题:emplace_back、push_back、reserve、resize 的区别

下面是一篇可以直接发到博客园的笔记。


C++ 容器常见问题:emplace_back、push_back、reserve、resize 的区别

最近看 C++ 代码时,经常遇到下面几个接口:

emplace_back
push_back
reserve
resize

它们在 std::vector 中非常常见,但含义并不一样。这里以 std::vector 为例做一个整理。


1. push_back 和 emplace_back

假设有一个结构体:

struct Point {double x;double y;double z;Point(double x, double y, double z): x(x), y(y), z(z) {}
};

定义一个 vector

std::vector<Point> points;

2. push_back:把已有对象放进去

push_back 的含义是:向容器末尾添加一个已经构造好的对象

例如:

Point p(1.0, 2.0, 3.0);
points.push_back(p);

这个过程可以理解为:

先创建对象 p
再把 p 拷贝到 vector 里面

也可以这样写:

points.push_back(Point(1.0, 2.0, 3.0));

这里会先构造一个临时对象:

Point(1.0, 2.0, 3.0)

然后再把这个临时对象移动到 vector 中。


3. emplace_back:在容器内部直接构造

emplace_back 的含义是:直接在容器末尾构造对象

例如:

points.emplace_back(1.0, 2.0, 3.0);

这行代码会把参数:

1.0, 2.0, 3.0

直接传给 Point 的构造函数:

Point(double x, double y, double z)

也就是说,它相当于直接在 vector 的内部空间中构造一个 Point 对象。


4. push_back 和 emplace_back 的直观区别

可以这样理解:

push_back:先在外面造好对象,再放进容器
emplace_back:直接在容器里面造对象

例如:

points.push_back(Point(1.0, 2.0, 3.0));

大致过程是:

构造临时 Point 对象
移动到 vector 中
销毁临时对象

而:

points.emplace_back(1.0, 2.0, 3.0);

大致过程是:

直接在 vector 内部构造 Point 对象

所以在某些情况下,emplace_back 可以减少一次临时对象构造或移动。


5. emplace_back 并不总是更好

如果已经有一个对象:

Point p(1.0, 2.0, 3.0);

那么下面两种写法差别并不大:

points.push_back(p);
points.emplace_back(p);

因为此时传进去的已经是一个 Point 对象。

如果要表达“把已有对象放进容器”,通常 push_back 更直观:

points.push_back(p);

如果想把对象移动进去,可以写:

points.push_back(std::move(p));

或者:

points.emplace_back(std::move(p));

6. push_back 和 emplace_back 的简单选择

可以记住这个原则:

已有对象:优先用 push_back
直接构造对象:优先用 emplace_back

例如:

std::vector<Point> points;Point p(1.0, 2.0, 3.0);
points.push_back(p);              // 已有对象,放进去points.emplace_back(4.0, 5.0, 6.0); // 直接在容器中构造

7. reserve 和 resize

reserveresize 都和 vector 的容量有关,但它们影响的东西不同。

理解它们之前,需要先区分两个概念:

size:当前实际有多少个元素
capacity:当前已分配的内存最多能容纳多少个元素

例如:

std::vector<int> v;

刚创建时通常是:

size = 0
capacity = 0

8. reserve:只预留空间,不创建元素

reserve(n) 的含义是:提前分配可以容纳 n 个元素的空间,但不改变元素数量

例如:

std::vector<int> v;
v.reserve(10);

执行后:

v.size()     == 0
v.capacity() >= 10

注意:size 仍然是 0

也就是说,vector 里面还没有真正的元素。

因此不能这样写:

v[0] = 123; // 错误

因为 v[0] 这个元素还不存在。

正确做法是继续使用 push_backemplace_back 添加元素:

std::vector<int> v;
v.reserve(10);v.push_back(1);
v.push_back(2);
v.push_back(3);

此时:

v.size()     == 3
v.capacity() >= 10

9. resize:改变元素数量

resize(n) 的含义是:把 vector 的元素数量改成 n 个

例如:

std::vector<int> v;
v.resize(10);

执行后:

v.size()     == 10
v.capacity() >= 10

这时 vector 里面真的有 10 个元素。

对于 int 来说,这些元素会被初始化为 0

std::vector<int> v;
v.resize(3);// v 中现在有 3 个元素:0, 0, 0

所以可以直接通过下标访问:

v[0] = 123;
v[1] = 456;
v[2] = 789;

10. reserve 和 resize 的核心区别

下面这段代码:

std::vector<int> a;
a.reserve(10);

结果是:

a.size()     == 0
a.capacity() >= 10

不能访问:

a[0] = 1; // 错误

因为还没有元素。


下面这段代码:

std::vector<int> b;
b.resize(10);

结果是:

b.size()     == 10
b.capacity() >= 10

可以访问:

b[0] = 1; // 正确

因为元素已经真实存在。


11. 常见错误:reserve 后直接用下标访问

很多人容易写出下面这种代码:

std::vector<int> v;
v.reserve(10);for (int i = 0; i < 10; ++i) {v[i] = i;   // 错误
}

问题在于:

v.reserve(10);

只是预留了空间,并没有创建 10 个元素。

正确写法之一:

std::vector<int> v;
v.reserve(10);for (int i = 0; i < 10; ++i) {v.push_back(i);
}

或者使用 resize

std::vector<int> v;
v.resize(10);for (int i = 0; i < 10; ++i) {v[i] = i;
}

12. 什么时候用 reserve

当你知道接下来大概要插入多少个元素,并且会使用 push_backemplace_back 追加时,适合使用 reserve

例如:

std::vector<Point> points;
points.reserve(10000);for (int i = 0; i < 10000; ++i) {points.emplace_back(i, i + 1, i + 2);
}

这样可以减少 vector 扩容次数,提高性能。

适合场景:

我要不断追加元素
我提前知道大概数量
我想减少扩容次数

13. 什么时候用 resize

当你希望 vector 立刻拥有指定数量的元素,并且后面要通过下标访问时,适合使用 resize

例如:

std::vector<int> values;
values.resize(10);for (int i = 0; i < 10; ++i) {values[i] = i;
}

适合场景:

我需要立刻创建 n 个元素
我要用下标直接访问或修改

需要注意的是,resize(n) 会真的构造 n 个元素。
如果元素类型比较复杂,构造成本可能比较高。


14. 和点云代码的联系

如果代码中有类似这样的容器:

std::vector<Generic3dPoint*> points;

那么:

points.reserve(n);

表示:

提前给 vector 分配能容纳 n 个指针的空间
但 points 当前还没有 n 个元素

后面通常还需要:

points.push_back(pointPtr);

或者:

points.emplace_back(pointPtr);

如果写的是:

points.resize(n);

则表示:

points 中现在真的有 n 个指针元素

更安全的写法通常是:

points.resize(n, nullptr);

意思是:

创建 n 个元素,每个元素都是 nullptr

这样可以避免未初始化指针带来的风险。


15. 总结

四个接口可以这样记:

push_back(obj)
把一个已经构造好的对象放进容器emplace_back(args...)
用参数直接在容器内部构造对象reserve(n)
预留能放 n 个元素的空间,但不创建元素resize(n)
把容器元素数量改成 n,真的创建或删除元素

常见推荐写法:

std::vector<Point> points;
points.reserve(10000);for (int i = 0; i < 10000; ++i) {points.emplace_back(i, i + 1, i + 2);
}

这段代码的含义是:

先预留空间,避免频繁扩容
再逐个在容器内部构造 Point 对象

这是 C++ 中使用 std::vector 时比较常见、也比较高效的写法。

http://www.rkmt.cn/news/1305725.html

相关文章:

  • 2026年论文降AI率教程:专家亲授的去AI痕迹技巧 - 降AI实验室
  • 5.12 vue工程化
  • 跑了6家嵌入式培训机构,说说我的真实感受(2026年)
  • 2026年4月优秀的双组份密封胶厂商口碑推荐,聚乙烯闭孔泡沫板/聚硫密封胶/钢边止水带,双组份密封胶品牌口碑推荐 - 品牌推荐师
  • 99.鄂尔多斯报考CPPM与SCMP,职场进阶优选众智商学院 - 众智商学院课程中心
  • 97.桂林报考CPPM与SCMP,职场进阶优选众智商学院 - 众智商学院课程中心
  • 服务器感染挖矿病毒后如何彻底清理定时任务与隐藏进程?
  • 96.阜阳报考CPPM与SCMP,职场进阶优选众智商学院 - 众智商学院课程中心
  • 长沙:报考中质协六西格玛黑带和绿带指定报考机构推荐 - 众智商学院课程中心
  • 2026年5月18日欧米茄售后服务中心最新电话地址查询 - 速递信息
  • 2026 太仓黄金回收门店测评|5 家主流门店硬核 PK,闲置金变现不踩坑 - 速递信息
  • 破解过流继电器校验低效难题:3P现场精准校验方法论如何保障机组安全? - 速递信息
  • 2026年国产雨鞋品牌推荐:不同场景高口碑高性价比雨鞋测评 - 速递信息
  • 2026宁波黄金回收实测:我跑了3家店,终于找到靠谱的 - 生活测评君
  • 2026年|论文查重2%但AI率爆表?全网最全降AI率保姆级指南 - 降AI实验室
  • 呼和浩特仓库货架选购指南:从市场格局到厂家深度解析 - 品牌推广大师
  • MewUI 项目:面向 NativeAOT 的超轻量级.NET GUI 架构、底层图形管线与性能演进
  • 微信立减金别放过期!回收变现就用了这一招 - 京顺回收
  • STM32 W5500 DNS
  • 实验九
  • 上海专业膝关节置换医院排行:精准诊疗实力盘点 - 奔跑123
  • 第一次blog
  • 【西门子-tcp服务端】
  • 写日志!运营程序
  • 北京钢筋混凝土化粪池厂家实力排行及核心维度对比 - 奔跑123
  • 父组件逻辑 (App)
  • 2026年告别AI检测重复警报:快速降AI工具推荐 - 降AI实验室
  • API 网关鉴权超时导致请求失败 error code 504 怎么优化?
  • 北京地区水泥预制隔油池供应商综合排行实测分析 - 奔跑123
  • 5.11 axios的使用