【C++】C++11 vector 之 emplace_back() 使用场景简单剖析

2023-05-16

emplace 关键字是 C++11 的一个新特性。emplace_back()push_abck() 的区别是:push_back() 在向 vector 尾部添加一个元素时,首先会创建一个临时对象,然后再将这个临时对象移动或拷贝到 vector 中(如果是拷贝的话,事后会自动销毁先前创建的这个临时元素);而 emplace_back() 在实现时,则是直接在 vector 尾部创建这个元素,省去了移动或者拷贝元素的过程。

但是 emplace_back() 的这个特性是在任何场景都生效的吗?

本文对 vector 的 emplace_back() 方法的使用进行简单实验,加深对 emplace_back() 的理解,在使用时更得心应手。

之前也了解到 emplace_back() 方法比 vector_back() 方法效率高,原因是 emplace_back() 在向 vector 中插入元素时比 push_back() 少了一次移动构造或拷贝构造。但 emplace_back() 并不是任何场景效率都比 push_back() 高。

先说 结论

假设 vector 中元素类型是类类型,那么 emplace_back() 待添加的元素的类型是类中有参构造的参数类型时,emplace_back()push_back() 少一次移动或拷贝构造函数。而如果添加的元素是类类型的对象时,则和 push_back() 一样都只会调用一次移动构造函数或一次拷贝构造函数。

如下示例:

class Person {
public:
    int num;

public:
    Person (int num) {
        cout << __PRETTY_FUNCTION__ << endl;
        this->num = num;
    }

    Person (const Person & p) {
        cout << __PRETTY_FUNCTION__ << endl;
        this->num = p.num;
        cout << "num=" << this->num << endl;
    }

    Person (Person && p) noexcept {
        cout << __PRETTY_FUNCTION__ << endl;
        this->num = p.num;
        p.num = 0;
        cout << "num=" << this->num << endl;
    }
    
    ~Person () {
        cout << __PRETTY_FUNCTION__ << endl;
    }
};

void test01() {

    Person p1(1);
    vector<Person> v;

    cout << __LINE__ << endl;
    v.push_back(10);
    v.push_back(10);
    v.push_back(10);
    v.push_back(10);
    v.push_back(10);
    v.push_back(10);
    v.push_back(10);
    v.push_back(10);
    v.push_back(10);
    
    // 上面这些操作,是为了扩充 vector 的容量,从而不受容量已满开辟新空间时移动元素时调用相关构造函数对下面代码的影响

    cout << __LINE__ << endl;  // a
    v.push_back(10);  // 会调用一次有参构造+一次移动构造

    cout << __LINE__ << endl;
    v.emplace_back(20);  // 会调用一次有参构造

    cout << __LINE__ << endl;
    v.push_back(p1);  // 会调用一次拷贝构造
    
    cout << __LINE__ << endl;
    v.emplace_back(p1);  // 会调用一次拷贝构造
                               // b
}

int main(){
    test01();
    return 0;
}

上述代码 a-b 之间的执行结果如下:

175
Person::Person(int)
Person::Person(Person &&)
num=10
Person::~Person()
178
Person::Person(int)
181
Person::Person(const Person &)
num=1
184
Person::Person(const Person &)
num=1

可结合代码中的注释来理解执行结果。

由上实验可以看出:在向 vector 中添加元素时(假设元素类型是一个类类型):

  • 如果添加的是类类型的有参构造函数的参数类型对应的变量:
    • 如果是通过 push_back() 方式添加的,则会调用 一次有参构造 + 一次移动构造 ,如果移动构造不可用则为拷贝构造
    • 如果是通过 emplace_back() 方式添加的,则只会调用 一次有参构造
  • 如果添加的是类类型的变量:
    • 不论是通过 push_back() 方式还是 emplace_back() 方式,都只会调用 一次拷贝构造

另外,对上述测试代码做相关解释:

  1. 在定义一个 vector 之后,向其 push_back() 9 个元素的原因是:由于 vector 的容量是动态增长的(2倍于之前的容量),所以向 vector 中添加 9 个元素,使其容量扩大到 16 ,这样后续的代码在执行时则不会受到 vector 开辟新空间时需要将原来空间的元素移动到新空间时调用相关构造函数的影响,如不理解可参考代码的执行结果。

之前以为 emplace_back() 不论是插入何种元素都是会调用一次有参构造+一次移动或拷贝构造,但是现在发现这其实是不准确的。另外,我本人在使用时其实是不会以有参构造中的参数类型的变量作为实参来使用 push_back() 的,个人感觉这种方式不易于理解,我更习惯于直接使用直接添加类类型的变量这种方式。但这种方式需要提前构造好待添加的类对象,这个操作也会调用一次构造函数,所以直接添加类类型的变量并不会提高效率。所以如果想要提高效率可以使用如下方式:

  • emplace_back(int num) 只会调用一次移动构造或拷贝构造
  • push_back(std::move(Person p))emplace_back(std::move(Person p)) 只会调用一次移动构造或拷贝构造
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】C++11 vector 之 emplace_back() 使用场景简单剖析 的相关文章

  • 根据向量元素的数量截断数据框

    我有一个数据框df 包含三个向量 subject condition value 01 A 12 01 A 6 01 B 10 01 B 2 02 A 5 02 A 11 02 B 3 02 B 5 02 B 9 主题 01 有四个观察值
  • decltype(some_vector)::size_type 不能用作模板参数

    以下类无法编译 template
  • 使用交换和弹出迭代时擦除向量中的元素

    我想在迭代向量时删除某些元素 但下面的代码会导致 向量迭代器不可递增 断言失败 for auto iter vec begin iter vec end iter if iter isDead std swap iter vec back
  • STL:为向量编写“where”运算符

    我需要根据几个布尔谓词找到向量中的索引 ex vector
  • SVG/矢量图室内导航路由

    我一直在网上搜索有关如何为基于 SVG 的室内平面图实现我自己的点对点导航系统的教程或方法 我已经在网上搜索过 但唯一的选项适用于谷歌地图 不过 我使用 Illustrator 创建了地图 并使用路径 矢量作为 SVG 图像 我不需要为用户
  • 如何从文本文件读取数据并将其推回向量?

    我有一个文本文件 test txt 它存储了我的数据 如下所示 每个分隔符字段之间有一个空格 代码 名称 Coy 045 Ted Coy1 054 Red Coy2 我如何从文件中读取这些数据并将其插入向量中 vector
  • gnuplot 动画 2D 矢量场

    我正在尝试使用 gnuplot 制作 2D 矢量动画 我想显示一行 即一次显示一个向量 我的数据结构如下 它们x y u v 2 24448 0 270645 1 00 1 00 3 24448 0 270645 0 500 1 20 我可
  • 我如何从 Rust 的 Vec 中获取项目?

    我正在寻找一种方法consumes a Vec并返回一个元素 无需恢复的开销Vec的不变量的方式remove and swap remove do fn take
  • 为什么在用 size 声明的向量上使用 Push_back 会导致向量为零?

    我制作了一个恒定大小的向量来存储负值 然后打印我得到的所有值都是零 我只是想知道为什么它不存储负值 include
  • 添加到 std::vector 的中间

    有没有办法将值添加到 a 的中间vector在 C 中 假设我有 vector
  • 使用步骤 c++ 构建向量

    是否可以在不使用 C 中的循环的情况下以固定步骤创建从一个值到另一个值的向量 例如 我想用步长 0 5 构建一个从 1 到 10 的向量 在 MATLAB 中我可以按如下方式执行此操作 vector 1 0 5 10 c 中有类似的东西吗
  • 方案中的多维向量?

    我之前问过一个关于方案中数组的问题 结果它们被称为向量 但在其他方面基本上与您期望的相同 有没有一种简单的方法可以在 PLT 方案中处理多维 arrays 向量 出于我的目的 我想要一个名为make multid vector或者其他的东西
  • 从向量中删除向量::end

    当我使用时它工作正常吗 什么也不做 vector
  • 矩阵向量变换

    我正在编写一个代码来制作软件蒙皮器 骨骼 皮肤动画 并且我正处于 优化 阶段 蒙皮器工作得很好 并且在 Core 上 1 09 毫秒内对 4900 个三角形网格与 22 个骨骼进行蒙皮Duo 2 Ghz 笔记本 我需要知道的是 1 有人可以
  • 采用 std::vector 或 std::array 的模板函数

    我有一个函数 当前接受 2 个向量 其中可以包含任何普通的旧数据 template
  • 为什么这些向量不相等?

    我创建了两个向量 并用push back填充另一个向量 用索引填充另一个向量 我希望这些是相等的 但事实并非如此 有人可以解释一下这是为什么吗 include
  • 尝试将元素推入向量

    在头文件 我没有编写 中 已经定义了一个结构体 如下所示 struct MemoryMessage public boost counted base public FastAlloc explicit MemoryMessage Memo
  • 确定向量中是否存在元素的最有效方法

    我有几种算法取决于确定元素是否存在于向量中的效率 在我看来 这 in 这相当于is element 应该是最有效的 因为它只返回一个布尔值 在测试了几种方法之后 令我惊讶的是 这些方法是迄今为止效率最低的 以下是我的分析 随着向量大小的增加
  • R:根据元素长度从向量中删除元素

    如何根据字符串的字符数或长度从字符串向量中删除元素 df lt c asdf fweafewwf af aewfawefwef awefWEfawefawef gt df 1 asdf fweafewwf af aewfawefwef aw
  • 通过引用传递向量

    如果我在一个类中有一个对象向量 我想在另一个类中更改它 我会尝试通过引用传递所有信息 我究竟需要通过引用传递什么 向量 物体 两个都 本质上我要问的是 它们之间有什么区别 vector blah A reference to a vecto

随机推荐