将 emplace_back()
和 push_back()
中区别最大的程序拎出来看:
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, std::forward<_Args>(__args)...); // emplace_back() _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x); // push_back()
对于 std::forward()
函数而言,本质上是一个类型转换函数,它的声明函数如下所示:
/** * 以下程序来自STL源码 bits/move.h * @brief Forward an lvalue. * @return The parameter cast to the specified type. * * This function is used to implement "perfect forwarding". */ template<typename _Tp> constexpr _Tp &&forward(typename std::remove_reference<_Tp>::type &__t) noexcept { return static_cast<_Tp &&>(__t); }
/** * Created by Xiaozhong on 2020/9/3. * Copyright (c) 2020/9/3 Xiaozhong. All rights reserved. */ #include <vector> #include <iostream> using namespace std; class Person { int _age; public: Person(int age) : _age(age) { cout << "Construct a person." << _age << endl; } Person(const Person &p) : _age(p._age) { cout << "Copy-Construct" << _age << endl; } Person(const Person &&p) noexcept: _age(p._age) { cout << "Move-Construct" << _age << endl; } }; #define TEST_EMPLACE_BACK //#define TEST_PUSH_BACK int main() { vector<Person> person; auto p = Person(1); // >: Construct a person.1 #ifdef TEST_EMPLACE_BACK person.emplace_back(move(p)); // >: Move-Construct1 person.emplace_back(2); /** * >: Construct a person.2 // 构建一个新的元素 * >: Move-Construct1 // 拷贝之前的元素过来,这个时候用的是 Person(const Person &&p) */ #endif #ifdef TEST_PUSH_BACK person.push_back(p); /** * >: Copy-Construct1 因为容器扩容,需要把前面的元素重新添加进来,因此需要拷贝 */ #endif }
root@ubuntu:~/c++# g++ -std=c++11 push.cpp -o push root@ubuntu:~/c++# ./push Construct a person.1 Move-Construct1 Construct a person.2 Move-Construct1 root@ubuntu:~/c++#
#include <vector> #include <iostream> using namespace std; class Person { int _age; public: Person(int age) : _age(age) { cout << "Construct a person." << _age << endl; } Person(const Person &p) : _age(p._age) { cout << "Copy-Construct" << _age << endl; } Person(const Person &&p) noexcept: _age(p._age) { cout << "Move-Construct" << _age << endl; } }; #define TEST_EMPLACE_BACK //#define TEST_PUSH_BACK int main() { vector<Person> person; auto p = Person(1); // >: Construct a person.1 #ifdef TEST_EMPLACE_BACK cout << "back " << endl; person.emplace_back(p); cout << "back move" << endl; person.emplace_back(move(p)); // >: Move-Construct1 cout << "back 2" << endl; person.emplace_back(2); /** * >: Construct a person.2 // 构建一个新的元素 * >: Move-Construct1 // 拷贝之前的元素过来,这个时候用的是 Person(const Person &&p) */ #endif #ifdef TEST_PUSH_BACK person.push_back(p); /** * >: Copy-Construct1 因为容器扩容,需要把前面的元素重新添加进来,因此需要拷贝 */ #endif }
root@ubuntu:~/c++# g++ -std=c++11 push.cpp -o push root@ubuntu:~/c++# ./push Construct a person.1 back Copy-Construct1 back move Move-Construct1 Move-Construct1 back 2 Construct a person.2 Move-Construct1 Move-Construct1
#include <vector> #include <iostream> using namespace std; class Person { int _age; public: Person(int age) : _age(age) { cout << "Construct a person." << _age << endl; } Person(const Person &p) : _age(p._age) { cout << "Copy-Construct" << _age << endl; } Person(const Person &&p) noexcept: _age(p._age) { cout << "Move-Construct" << _age << endl; } }; //#define TEST_EMPLACE_BACK #define TEST_PUSH_BACK int main() { vector<Person> person; auto p = Person(1); // >: Construct a person.1 #ifdef TEST_EMPLACE_BACK person.emplace_back(move(p)); // >: Move-Construct1 person.emplace_back(2); /** * >: Construct a person.2 // 构建一个新的元素 * >: Move-Construct1 // 拷贝之前的元素过来,这个时候用的是 Person(const Person &&p) */ #endif #ifdef TEST_PUSH_BACK person.push_back(p); /** * >: Copy-Construct1 因为容器扩容,需要把前面的元素重新添加进来,因此需要拷贝 */ #endif }
root@ubuntu:~/c++# g++ -std=c++11 push.cpp -o push root@ubuntu:~/c++# ./push Construct a person.1 Copy-Construct1 root@ubuntu:~/c++#
emplace_back()
函数在原理上比 push_back()
有了一定的改进,包括在内存优化方面和运行效率方面。内存优化主要体现在使用了就地构造(直接在容器内构造对象,不用拷贝一个复制品再使用)+强制类型转换的方法来实现,在运行效率方面,由于省去了拷贝构造过程,因此也有一定的提升。
#include <vector> #include <iostream> using namespace std; class Person { int _age; public: Person(int age) : _age(age) { cout << "Construct a person." << _age << endl; } Person(const Person &p) : _age(p._age) { cout << "Copy-Construct" << _age << endl; } Person(const Person &&p) noexcept: _age(p._age) { cout << "Move-Construct" << _age << endl; } }; //#define TEST_EMPLACE_BACK #define TEST_PUSH_BACK int main() { vector<Person> person; auto p = Person(1); // >: Construct a person.1 #ifdef TEST_EMPLACE_BACK cout << "back " << endl; person.emplace_back(p); cout << "back move" << endl; person.emplace_back(move(p)); // >: Move-Construct1 cout << "back 2" << endl; person.emplace_back(2); /** * >: Construct a person.2 // 构建一个新的元素 * >: Move-Construct1 // 拷贝之前的元素过来,这个时候用的是 Person(const Person &&p) */ #endif #ifdef TEST_PUSH_BACK person.push_back(p); cout << "second push back " << endl; person.push_back(p); cout << "third push back " << endl; person.push_back(p); /** * >: Copy-Construct1 因为容器扩容,需要把前面的元素重新添加进来,因此需要拷贝 */ #endif }
root@ubuntu:~/c++# ./push Construct a person.1 Copy-Construct1 second push back Copy-Construct1 -------------
Move-Construct1 third push back Copy-Construct1 -------插入的时候总是copy Move-Construct1 Move-Construct1