根据cppreference
push_back()分为两步
1) 新元素被初始化为值的副本。
2) 值被移动到新元素中。
如果传入的是vector存储类型的值,那么将根据具体实现(赋值构造和移动构造)将元素移动到对应的位置
class MyClass {
public:
// 默认构造函数
MyClass() :num(0), st("default") {
std::cout << "Constructed with default constructor." << std::endl;
}
MyClass(int num, string st) :num(num), st(st) {
std::cout << "Constructed with default constructor." << std::endl;
}
// 析构函数
~MyClass() {
std::cout << "Destructed." << std::endl;
}
// 拷贝构造函数
MyClass(const MyClass& other) :num(other.num), st(other.st) {
std::cout << "Constructed with copy constructor." << std::endl;
}
// 拷贝赋值运算符
MyClass& operator=(const MyClass& other) {
std::cout << "Copy assignment operator called." << std::endl;
if (this != &other) { // 避免自赋值
num = other.num;
st = other.st;
}
return *this;
}
// 移动构造函数
MyClass(MyClass&& other) noexcept :num(std::move(other.num)), st(std::move(other.st)) {
std::cout << "Constructed with move constructor." << std::endl;
}
// 移动赋值运算符
MyClass& operator=(MyClass&& other) noexcept {
std::cout << "Move assignment operator called." << std::endl;
if (this != &other) {
num = std::move(other.num);
st = std::move(other.st);
}
return *this;
}
private:
string st;
int num;
};
int main(){
vector<MyClass> v;
// 防止插入的过程中发生扩容,大量调用类的移动构造
v.reserve(10);
// 调用默认构造
auto a = MyClass();
// 传递左值, 调用拷贝构造
v.push_back(a);
// 传递右值, 调用移动构造
v.push_back(std::move(a));
}
/*
实验结果:
Constructed with default constructor.
Constructed with copy constructor.
Constructed with move constructor.
Destructed.
Destructed.
Destructed.
*/
如果传入的是存储类型的构造参数,且类支持隐式构造,那么将先进行隐式构造(构造完是右值),后移动到vector末尾
int main(){
vector<MyClass> v;
// 防止插入的过程中发生扩容,大量调用类的移动构造
v.reserve(10);
// 单参数和双参数的隐式构造
v.push_back(1);
v.push_back({2, "xxx"});
}
/*
Constructed with default constructor.
Constructed with move constructor.
Destructed.
Constructed with default constructor.
Constructed with move constructor.
Destructed.
Destructed.
Destructed.
*/
对于emplace_back()
"该元素是通过 std::allocator_traits::construct 构造的,通常使用placement-new 在容器提供的位置就地构造元素。参数 args... 作为 std::forward<Args>(args)... 转发到构造函数。"
意思就是,emplace_back()会首先尝试使用存储元素类型的构造函数,就地构造
而传入emplace_back()的参数会完美转发给构造函数
int main(){
vector<MyClass> v;
// 防止插入的过程中发生扩容,大量调用类的移动构造
v.reserve(10);
// 单参数和双参数的隐式构造
v.emplace_back(1);
v.emplace_back(2, "xxx");
}
/*
Constructed with default constructor.
Constructed with default constructor.
Destructed.
Destructed.
*/
以上,只需要一次构造就可以,并不需要将元素”移动“到vector指定位置。
除了就地构造外,和push_back()是相同的
int main(){
vector<MyClass> v;
// 防止插入的过程中发生扩容,大量调用类的移动构造
v.reserve(10);
auto a = MyClass();
// 传递左值, 调用拷贝构造
v.emplace_back(a);
// 传递右值, 调用移动构造
v.emplace_back(std::move(a));
}
/*
Constructed with default constructor.
Constructed with copy constructor.
Constructed with move constructor.
Destructed.
Destructed.
Destructed.
*/
需要注意的是
// 不报错
v.emplace_back(1);
// 报错 error: no matching function for call to 'std::vector<MyClass>::emplace_back(<brace-enclosed initializer list>)'
v.emplace_back({1, "xx"});
这是因为,emplace_back()首先会尝试使用存储对象的构造函数原地构造,而{1, "xx"}会自动转换成initializer_list,并经过完美转发,MyClass并没有接受参数initializer_list的构造参数,报错