欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【C++】C++11 vector 的 push_back() 和 emplace_back() 使用

程序员文章站 2022-03-21 14:33:24
...

emplace 关键字是 C++11 的一个新特性。amplace_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() 少一次移动或拷贝构造函数。

如下示例:

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() 的,个人感觉这种方式不易于理解,我更习惯于直接使用直接添加类类型的变量这种方式。

相关标签: C++ c++