1−复制构造函数又称拷贝构造函数{\red{1-复制构造函数又称拷贝构造函数}}1−复制构造函数又称拷贝构造函数
2−赋值构造函数必须传引用,若传值则会无限递归,爆堆栈{\red{2-赋值构造函数必须传引用,若传值则会无限递归,爆堆栈}}2−赋值构造函数必须传引用,若传值则会无限递归,爆堆栈
C++中,三种调用复制构造函数的情况:
一个对象以值传递的方式传入函数体{\red{一个对象以值传递的方式传入函数体}}一个对象以值传递的方式传入函数体
一个对象以值传递方式从函数返回{\orange{一个对象以值传递方式从函数返回}}一个对象以值传递方式从函数返回
一个对象需要通过另一个对象初始化{\green{一个对象需要通过另一个对象初始化}}一个对象需要通过另一个对象初始化
自定义复制构造函数的意义:
当类对象的成员使用堆内存,且可能会出现上述三种情况时,需要自定义复制构造函数。
默认复制构造函数只是指针内容上的复制,但并不会复制堆内存。
复制构造函数与赋值函数的区别:
复制构造是用一个对象来初始化一块内存区域{\red{复制构造是用一个对象来初始化一块内存区域}}复制构造是用一个对象来初始化一块内存区域
赋值函数是对于一个已经初始化的对象来修改{\orange{赋值函数是对于一个已经初始化的对象来修改}}赋值函数是对于一个已经初始化的对象来修改
复制构造和析构函数是成对儿的{\red{复制构造和析构函数是成对儿的}}复制构造和析构函数是成对儿的
赋值函数不会与析构函数成对儿{\green{赋值函数不会与析构函数成对儿}}赋值函数不会与析构函数成对儿
复制构造大多数情况是深拷贝{\red{复制构造大多数情况是深拷贝}}复制构造大多数情况是深拷贝
赋值函数大多数是引用{\blue{赋值函数大多数是引用}}赋值函数大多数是引用
关于复制构造《编译器版本,Apple clang 11.0.0》:
示例一,将参数返回重新赋值,调用复制构造函数{\red{示例一,将参数返回重新赋值,调用复制构造函数}}示例一,将参数返回重新赋值,调用复制构造函数
class A{
public:
int v; // 成员变量
A(int t){ // 构造函数
v = t;
cout<<"A construction "<<v<<endl;
}
A(A &a){ // 复制构造
v = a.v + 5;
cout<<"A copy construction "<<v<<endl;
}
A& operator = (const A &a){ // 赋值函数
this->v = a.v + 7;
cout<<this->v<<" assignment "<<a.v<<endl;
return *this;
}
~A(){ // 析构函数
cout<<"A destruction "<<v<<endl;
}
};
A fun(A a){ return a; }
int main(){
A a(3);
a = fun(a);
return 0;
}
输出{\orange{输出}}输出
A construction 3{\orange{A\ construction\ 3}}A construction 3
A copy construction 8{\orange{A\ copy\ construction\ 8}}A copy construction 8
A copy construction 13{\orange{A\ copy\ construction\ 13}}A copy construction 13
20 assignment 13{\orange{20\ assignment\ 13}}20 assignment 13
A destruction 13{\orange{A\ destruction\ 13}}A destruction 13
A destruction 8{\orange{A\ destruction\ 8}}A destruction 8
A destruction 20{\orange{A\ destruction\ 20}}A destruction 20
说明一下执行顺序:{\red{说明一下执行顺序:}}说明一下执行顺序:
- A a(3); 调用自定义构造函数A(int),成员为3
- main函数变量“a”传入fun函数,复制构造fun函数参数“a”,此时成员为8
- 将fun函数参数“a”返回,复制构造临时变量“b”,此时成员为13,代码变为a = b;
- 由于main函数变量"a"已经初始化,此时赋值函数将b赋值给a,a成员为20
- a = fun(a);执行完毕,fun函数退出函数栈,按栈的方式逐一销毁fun函数变量,临时变量b<成员13>销毁,参数a<成员8>销毁
- main函数执行完毕,退出函数栈,main函数变量a<成员20>销毁
示例二,将函数内变量返回,不调用复制构造{\red{示例二,将函数内变量返回,不调用复制构造}}示例二,将函数内变量返回,不调用复制构造
class A{
public:
int v;
A(int t){
v = t;
cout<<"A construction "<<v<<endl;
}
A(A &a){
v = a.v + 5;
cout<<"A copy construction "<<v<<endl;
}
A& operator = (const A &a){
this->v = a.v + 7;
cout<<this->v<<" assignment "<<a.v<<endl;
return *this;
}
~A(){
cout<<"A destruction "<<v<<endl;
}
};
A fun(){
A a(2);
return a;
}
int main(){
A a(3);
a = fun();
return 0;
}
输出{\orange{输出}}输出
A construction 3{\orange{A\ construction\ 3}}A construction 3
A construction 2{\orange{A\ construction\ 2}}A construction 2
9 assignment 2{\orange{9\ assignment\ 2}}9 assignment 2
A destruction 2{\orange{A\ destruction\ 2}}A destruction 2
A destruction 9{\orange{A\ destruction\ 9}}A destruction 9
这里返回a不调用复制构造感觉是编译器做的优化,我也不知道为啥?{\red{这里返回a不调用复制构造感觉是编译器做的优化,我也不知道为啥?}}这里返回a不调用复制构造感觉是编译器做的优化,我也不知道为啥?
情况三,参数返回构造新示例,返回值不生成临时变量{\red{情况三,参数返回构造新示例,返回值不生成临时变量}}情况三,参数返回构造新示例,返回值不生成临时变量
class A{
public:
static int num;
A(){num++;}
void print(){cout<<num<<endl;}
~A(){num--;print();}
};
int A::num = 0;
A fun(A b){
b.print();
return b;
}
int main(){
A a;
a.print();
A c = fun(a);
c.print();
return 0;
}
输出:{\orange{输出:}}输出: 1 1 0 0 -1 -2
也是编译器优化?哭晕在厕所,搞不懂{\red{也是编译器优化?哭晕在厕所,搞不懂}}也是编译器优化?哭晕在厕所,搞不懂
2019−07−04更新:{\green{2019-07-04更新:}}2019−07−04更新:
当右值是临时变量时,构造可能采用移动构造优化,C++11新特性。
移动构造,浅拷贝且临时变量空间不销毁。