c++构造函数的调用
c++11中构造函数包括:
- 默认构造函数
- 拷贝构造函数
- 移动构造函数
- 拷贝赋值函数
- 移动赋值函数
那么请思考下面的一个案例中其调用的顺序是怎样的:
class A {
public:
A() {
printf("A()--默认构造\n");
}
~A() {
printf("~A()--析构函数\n");
}
A(const A &a) {
printf("A(const A& a)--复制构造\n");
}
A(A &&a) {
printf("A(A&& a)--移动构造\n");
}
A &operator=(const A &a) {
printf("operator=(const A& a)--赋值构造\n");
return *this;
}
A &operator=(A &&a) {
printf("operator=(A&& a)--移动赋值构造\n");
return *this;
}
};
int main() {
std::vector<A> vec;
vec.reserve(2);
for (int i = 0; i < 4; ++i) {
cout<<i<<endl;
vec.push_back(A());
}
cout<<"------"<<endl;
vec.resize(8);
cout<<"-------"<<endl;
return 0;
}
输出为:
0
A()--默认构造
A(A&& a)--移动构造
~A()--析构函数
1
A()--默认构造
A(A&& a)--移动构造
~A()--析构函数
2
A()--默认构造
A(A&& a)--移动构造
A(const A& a)--复制构造
A(const A& a)--复制构造
~A()--析构函数
~A()--析构函数
~A()--析构函数
3
A()--默认构造
A(A&& a)--移动构造
~A()--析构函数
------
A()--默认构造
A()--默认构造
A()--默认构造
A()--默认构造
A(const A& a)--复制构造
A(const A& a)--复制构造
A(const A& a)--复制构造
A(const A& a)--复制构造
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
-------
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
需要重点关注的地方(为什么使用复制构造而不用移动构造呢?):
当重新分配vector的内存时 vector将元素从旧空间移动到新内存中,虽然移动操作通常不会抛出异常,但是抛出异常也是允许的;因为A对象的移动构造没有标记为noexcept,为了避免移动失败的问题,使用了复制构造;
如果希望vector 重新分配内存这类情况下,对我们自定义类型的对象进行移动,而不是拷贝, 就必须显示的告诉标准库,我们的移动构造函数可以安全使用,那下面让我们将移动构造函数标记为noexcept:
class A {
public:
A() {
printf("A()--默认构造\n");
}
~A() {
printf("~A()--析构函数\n");
}
A(const A &a) {
printf("A(const A& a)--复制构造\n");
}
A(A &&a) noexcept{
printf("A(A&& a)--移动构造\n");
}
A &operator=(const A &a) {
printf("operator=(const A& a)--赋值构造\n");
return *this;
}
A &operator=(A &&a) {
printf("operator=(A&& a)--移动赋值构造\n");
return *this;
}
};
int main() {
std::vector<A> vec;
vec.reserve(2);
for (int i = 0; i < 4; ++i) {
cout<<i<<endl;
vec.push_back(A());
}
cout<<"------"<<endl;
vec.resize(8);
cout<<"-------"<<endl;
return 0;
}
输出为:
0
A()--默认构造
A(A&& a)--移动构造
~A()--析构函数
1
A()--默认构造
A(A&& a)--移动构造
~A()--析构函数
2
A()--默认构造
A(A&& a)--移动构造
A(A&& a)--移动构造
A(A&& a)--移动构造
~A()--析构函数
~A()--析构函数
~A()--析构函数
3
A()--默认构造
A(A&& a)--移动构造
~A()--析构函数
------
A()--默认构造
A()--默认构造
A()--默认构造
A()--默认构造
A(A&& a)--移动构造
A(A&& a)--移动构造
A(A&& a)--移动构造
A(A&& a)--移动构造
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
-------
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
~A()--析构函数
可以看到这次在vector重新分配内存后,是对对象进行了移动,而不是拷贝;
移动赋值运算符和移动构造时相同的。