C++: vector中emplace_back与push_back的区别详细解析

1. 相同点

  • 都是往容器(比如 std::vector, std::deque, std::list 等)末尾插入一个元素。

  • 插入完成后,容器的大小 (size()) 会增加 1。

  • 插入位置都是 容器的尾部,不能指定任意位置。

2. push_back 的语义

vector.push_back(value);
  • 作用:把一个 已经构造好的对象 加到容器末尾。

  • 参数:只能传入一个已经存在的对象,或者能隐式转换为容器元素类型的对象。

  • 过程(以 std::vector<int> 为例):

    1. 你先要有一个 int 类型的对象;

    2. push_back拷贝构造(或移动构造,如果传右值)一个新的对象到容器末尾。

例子:

std::vector<std::string> v;
std::string s = "hello";

// 1. 先构造 s
v.push_back(s);              // 拷贝构造
v.push_back(std::move(s));   // 移动构造
v.push_back("world");        // 临时对象 -> 移动/拷贝构造

3. emplace_back 的语义

vector.emplace_back(args...);
  • 作用:在容器尾部 就地(in-place)直接构造对象,而不是先构造再拷贝/移动。

  • 参数:是容器元素构造函数的参数,会直接传递给元素类型的构造函数。

  • 过程(仍以 std::vector<std::string> 为例):

    1. 在容器末尾的存储位置上,直接调用构造函数,用传入的参数构造对象;

    2. 避免了临时对象的产生以及拷贝/移动开销。

例子:

std::vector<std::string> v;

// 在 vector 尾部直接用构造函数参数 "hello" 构造一个 string 对象
v.emplace_back("hello");  // 相当于 v.push_back(std::string("hello")),但省掉了临时对象

4. 举例对比

push_back

std::vector<std::pair<int, std::string>> v;
std::pair<int, std::string> p(1, "abc");

// 需要先构造 p,再把 p 拷贝/移动到容器
v.push_back(p);               // 拷贝
v.push_back(std::move(p));    // 移动

emplace_back

std::vector<std::pair<int, std::string>> v;

// 直接在容器尾部构造 pair(1, "abc"),避免临时对象
v.emplace_back(1, "abc");

注意:emplace_back(1, "abc") 相当于调用 pair<int, string>(1, "abc") 构造函数。
push_back("abc") 在这个例子中甚至编译不过,因为参数类型不匹配。

5. 性能差异

  • push_back

    • 需要一个已经存在的对象;

    • 往容器里放的时候可能涉及 拷贝构造/移动构造

  • emplace_back

    • 直接调用构造函数,在容器内存里 原地构造对象

    • 可以省掉一次临时对象的构造与拷贝/移动,效率更高(尤其是对象比较复杂时差距明显)。

但要注意:

  • 对于 简单类型(如 int, double,性能差距几乎可以忽略。

  • 对于 复杂类型(如 std::string, std::mapemplace_back 可以避免多余开销。

6. 使用建议

  • 如果你 已经有一个对象,用 push_back 更自然:

std::vector<Person> v;
Person p("Tom", 20);
v.push_back(p);             // 拷贝
v.push_back(std::move(p));  // 移动
  • 如果你想 直接构造对象,避免中间的临时变量,用 emplace_back 更好:
std::vector<Person> v;
v.emplace_back("Tom", 20);  // 直接原地构造 Person("Tom", 20)

7. 容易踩坑的地方

  • 坑 1:emplace_back 不等于 push_back

std::vector<std::string> v;
v.push_back("hello");       // OK,隐式转换成 std::string
v.emplace_back("hello");    // OK,直接调用 string(const char*) 构造函数

std::vector<int> vi;
vi.push_back(3.5);          // OK,会把 double 转成 int 存进去
vi.emplace_back(3.5);       // OK,同样会调用 int 的构造函数,结果也是 int(3.5) -> 3
  • 坑 2:emplace_back 可能触发不同的构造函数
struct A {
    A(int, double) { std::cout << "A(int,double)\n"; }
    A(std::pair<int,double>) { std::cout << "A(pair)\n"; }
};

std::vector<A> v;
v.push_back({1, 2.0});       // 调用 A(pair),因为花括号推断为 pair
v.emplace_back(1, 2.0);      // 调用 A(int,double),因为参数直接传给构造函数

8. 总结表格

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值