1.1拷贝构造函数调用时机
概述:
C++中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
1. 使用一个已经创建完毕的对象来初始化一个新对象
#include<iostream>
#include<string>
using namespace std;
class h_idol
{
public:
//构造函数
h_idol() {
idol = "何运晨";
cout << "调用无参构造函数!" << endl;
}
h_idol(string a) {
idol = a;
cout << "调用有参构造函数!" << endl;
}
//(3)拷贝构造函数
h_idol(const h_idol& a) {
idol = a.idol;
cout << "调用拷贝构造函数!" << endl;
}
//析构函数
~h_idol()
{
cout << "调用了h_idol的析构函数" << endl;
}
public:
string idol;
};
void call_idol_1() {
h_idol idol02("道枝骏佑");//创建有参构造函数
h_idol idol03(idol02); //调用拷贝构造函数
h_idol idol04;
cout<< idol04.idol << endl;
idol04 = idol02;//不是调用拷贝构造函数,赋值操作
cout<< idol03.idol << endl;
}
int main()
{
call_idol_1();
system("pause");
return 0;
}
}
输出:
调用有参构造函数!
调用拷贝构造函数!
调用无参构造函数!
何运晨
道枝骏佑
调用了h_idol的析构函数
调用了h_idol的析构函数
调用了h_idol的析构函数
请按任意键继续. . .
2. 值传递的方式给函数参数传值
** 本质:**就是拷贝一个临时的副本出来,也就是调用拷贝构造函数来创建一个新的东西。
void change_idol(h_idol idol00)
{
idol01.idol = string("道枝骏佑");
cout << idol00.idol << endl;
}
void call_idol_1() {
h_idol idol01; //无参构造函数
change_idol(idol01);//实参传递给形参,这时候会调用拷贝构造函数,值传递只会拷贝出一个临时的副本,并不会影响下面的数据
cout << idol01.idol << endl;
输出:
调用无参构造函数!
调用拷贝构造函数!
道枝骏佑
调用了h_idol的析构函数
何运晨 //没有影响idol01的数据
调用了h_idol的析构函数
请按任意键继续. . .
3. 以值方式返回局部对象(存在争议)
h_idol change_idol()
{
h_idol idol00;
cout << (int *)&idol00 << endl;
cout << idol00.idol << endl;
return idol00; //局部对象有个特点,函数执行完之后就会被释放掉,返回的时候,它会根据diol00,创建一个新的对象然后返idol01回到外面的对象,故它返回的不是idol00。
}
void call_idol_1() {
h_idol idol01 = change_idol();
cout << (int *)&idol01 << endl;
cout << idol01.idol << endl;
}
输出:
调用无参构造函数!
0x6dfe6c
何运晨
0x6dfe6c
何运晨
调用了h_idol的析构函数
请按任意键继续. . .
1.2 构造函数调用规则
概述:
- 默认情况下,c++编译器至少给一个类上述添加3个构造函数
- 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
1.如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
class h_idol
{
public:
//构造函数
// h_idol() {
// cout << "调用无参构造函数!" << endl;
// }
h_idol(string a) {
idol = a;
cout << "调用有参构造函数!" << endl;
}
//(3)拷贝构造函数
// h_idol(const h_idol& a) {
// idol = a.idol;
// cout << "调用拷贝构造函数!" << endl;
// }
//析构函数
~h_idol()
{
cout << "调用了h_idol的析构函数" << endl;
}
public:
string idol;
};
void call_idol_1() {
//h_idol idol00;//错误提示:[Error] no matching function for call to 'h_idol::h_idol()'
h_idol idol01("何运晨"); //用户提供的有参
cout << idol01.idol << endl;
h_idol idol02(idol01);//此时如果用户没有提供拷贝构造,编译器会提供
cout << idol02.idol << endl;
}
输出:
调用有参构造函数!
何运晨
调用拷贝构造函数!
何运晨
调用了h_idol的析构函数
调用了h_idol的析构函数
请按任意键继续. . .
2.如果用户定义拷贝构造函数,c++不会再提供其他构造函数
- 如果用户自己没有提供默认构造,而调用了会出错
- 时如果用户自己没有提供有参,而调用了会出错
class h_idol
{
public:
//构造函数
// h_idol() {
// cout << "调用无参构造函数!" << endl;
// }
// h_idol(string a) {
// idol = a;
// cout << "调用有参构造函数!" << endl;
//}
//(3)拷贝构造函数
h_idol(const h_idol& a) {
idol = a.idol;
cout << "调用拷贝构造函数!" << endl;
}
//析构函数
~h_idol()
{
cout << "调用了h_idol的析构函数" << endl;
}
public:
string idol;
};
//调用无参构造函数
//h_idol change_idol()
// {
// h_idol idol00;
// cout << (int *)&idol00 << endl;
// cout << idol00.idol << endl;
// return idol00;
// }
void call_idol_1() {
h_idol idol00;//[Error] no matching function for call to 'h_idol::h_idol()'
h_idol idol01("何运晨");//[Error] no matching function for call to 'h_idol::h_idol(const char [7])'
}
1.2深拷贝和浅拷贝
- 浅拷贝:简单的赋值拷贝操作
- 深拷贝:在堆区重新申请空间,进行拷贝操作
#include<iostream>
#include<string>
using namespace std;
class h_idol
{
public:
//构造函数
h_idol() {
cout << "调用无参构造函数!" << endl;
}
h_idol(string a,double height) {
idol = a;
idol_height = new double(height);//把height创建在堆区,堆区的数据由程序员手动开辟和释放
cout << "调用有参构造函数!" << endl;
}
//(3)拷贝构造函数
h_idol(const h_idol& a) {
idol = a.idol;
//idol_height = a.idol_height 编译器就默认实现这行代码
idol_height = new double(*a.idol_height);
cout << "调用拷贝构造函数!" << endl;
}
//析构函数
~h_idol()
{
//析构代码,将堆区开辟的数据做释放操作
cout << "调用了h_idol的析构函数" << endl;
if(idol_height != NULL){
delete idol_height;
idol_height = NULL;
}
}
public:
string idol;
double * idol_height;
};
void call_idol_1() {
h_idol idol01("何运晨",1.85);
cout << "idol01身高:" << *idol01.idol_height << endl;
h_idol idol02(idol01);//idol01和idol02是栈区,先进后出,
//idol02先被释放,结果导致堆区的区域重复被释放
cout << "idol02身高:" << *idol02.idol_height << endl;
}
int main()
{
call_idol_1();
system("pause");
return 0;
}
输出:
调用有参构造函数!
idol01身高:1.85
调用拷贝构造函数!
idol02身高:1.85
调用了h_idol的析构函数
调用了h_idol的析构函数
请按任意键继续. . .
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题