作者:小王同学在积累
链接:https://www.zhihu.com/question/437657370/answer/1692846096
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
5. 构造函数和析构函数
5.1 构造函数和析构函数的作用
① 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
② 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
5.2 构造函数和析构函数的语法
① 构造函数语法:类名 () {}
1. 构造函数,没有返回值也不写void。
2. 函数名称与类名相同。
3. 构造函数可以有参数,因此可以重载。
4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次。
② 析构函数语法:~类名(){}
1. 析构函数,,没有返回值也不写void。
2. 函数名称与类名相同,在名称前加上符号。
3. 析构函数不可以有参数,因此不可以发生重载。
4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次。
#include <iostream>
using namespace std;
#include <string>
//对象的初始化和清理
//1、构造函数 进行初始化操作
class Person
{
public: //无论是构造函数还是析构函数都是在public作用域下
//1.1、构造函数
//没有返回值 不用写void
//函数名 与类名相同
//构造函数可以有参数,可以发生重载
//创建对象的时候,构造函数会自动调用,而且只调用一次
Person()
{
cout << "Person 构造函数的调用" << endl;
}
/*
如果你不写,编译器会自动创建一个,但是里面是空语句
Person()
{
}
*/
//1. 析构函数,,没有返回值也不写void。
//2. 函数名称与类名相同,在名称前加上符号。
//3. 析构函数不可以有参数,因此不可以发生重载。
//4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次。
~Person()
{
cout << "Person 析构函数的调用" << endl;
}
};
//构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现
void test01()
{
Person p; //创建对象的时候,自动调用构造函数
//这个对象p是一个局部变量,是在栈上的数据,test01执行完,释放这个对象
}
int main()
{
//方式一:
test01(); // 析构释放时机在test01运行完前,test01函数运行完后,里面的对象就被释放了
/*
方式二: //创建对象的时候,自动调用构造函数
Person p; //只有构造函数,没有析构函数,只有main函数结束完前,对象要释放掉了,才会调用析构函数
*/
system("pause");
return 0;
}
运行结果:
- Person 构造函数的调用
- Person 析构函数的调用
- 请按任意键继续. . .
5.3 构造函数的分类及调用
① 两种分类方式:
1. 按参数分为:有参构造和无参构造。
2. 按类型分为:普通构造和拷贝构造。
② 三种调用方式:
1. 括号法
2. 显示法
3. 隐式转换法
#include <iostream>
using namespace std;
//1构造函数的分类及调用
//分类
//按照参数分类:无参构造(默认构造) 和 有参构造
class Person
{
public:
//构造函数 编译器默认的构造函数是无参的
Person()
{
cout << "Person 无参构造函数的调用" << endl;
}
Person(int a)
{
age = a;
cout << "Person 有参构造函数的调用" << endl;
}
//拷贝构造函数
Person( const Person &p) //用引用的方式传进来,不能改变原来的对象的属性,所以用const
{
// 将传入的人人身上的所有属性,拷贝到我身上
cout << "Person 拷贝构造函数的调用" << endl;
age = p.age;
}
~Person()
{
cout << "Person 析构函数的调用" << endl;
}
int age;
};
//调用
void test01()
{
/*
//1、括号法
Person p1; //默认构造函数调用
Person p2(10); //有参构造函数
Person p3(p2); //拷贝构造函数
cout << "p2的年龄为:" << p2.age << endl;
cout << "p3的年龄为:" << p3.age << endl;
//注意事项1
//调用默认构造函数的时候,不要加()。
//下面这行代码,编译器会认为是一个函数的声明,像void func(),不会认为在创建对象。
//Person p1();
*/
/*
*
//2、显示法
Person p1; //创建一个对象,这个对象调用的是无参构造
Person p2 = Person(10); //有参构造 将匿名对象起了一个名称p2
Person p3 = Person(p2); //创建一个对象,这个对象调用的是拷贝构造
Person(10); //匿名对象 特点:当前行执行结束后,系统会立即回收匿名对象
cout << "aaaa" << endl; //通过打印时机可以得到:test还没结束,就运行析构函数了
//注意事项2
//不要利用拷贝构造函数 初始化匿名对象 编译器认为 Person(p3) 等价于 Person p3,
//编译器会认为这是一个对象的声明,而上面已经有一个p3了,Person p3 = Person(p2);因此编译器认为重定义了
Person(p3);
*/
//3、隐式转换法
Person p4 = 10; //相当于 写了 Person p4 = Person(10); 调用有参构造
Person p5 = p4; //调用拷贝构造
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
- Person 有参构造函数的调用
- Person 拷贝构造函数的调用
- Person 析构函数的调用
- Person 析构函数的调用
- 请按任意键继续. . .
5.4 拷贝构造函数调用时机
① C++中拷贝构造函数调用时机通常有三种情况。
1. 使用一个已经创建完毕的对象来初始化一个新对象。
2. 值传递的方式给函数参数传值。
3. 以值方式返回局部对象。
#include <iostream>
using namespace std;
//拷贝构造函数调用时机
//1、使用一个已经创建完毕的对象来初始化一个新对象
//2、值传递的方式给函数参数传值
//3、值方式返回局部对象
class Person
{
public:
Person()
{
cout << "Person 默认构造函数调用" << endl;
}
Person(int age)
{
m_Age = age;
cout << "Person 有参构造函数调用" << endl;
}
Person(const Person& p)
{
m_Age = p.m_Age;
cout << "Person 拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 析构函数调用" << endl;
}
int m_Age;
};
//1、使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{
Person p1(20);
Person p2(p1);
cout << "p2的年龄为:" << p2.m_Age << endl;
}
//2、值传递的方式给函数参数传值
void doWork(Person p)
{
}
void test02()
{
Person p;
doWork(p); //实参传给形参的时候,会调用拷贝构造函数,这个是值传递,是一个临时的副本
//拷贝出去的p和原来的p 不是一个p
}
//3、值方式返回局部对象
Person doWork2() //返回值类型为Person对象
{
Person p1; //局部对象
cout << (int*)&p1 << endl;
return p1; //以值的方式返回一个拷贝的对象给外部,拷贝出一个对象p1'与原对象p1不一样,调用拷贝构造函数
//程序运行结束,释放原p1,调用析构函数
}
void test03()
{
Person p = doWork2(); //这里没有调用拷贝构造函数,直接用p接收拷贝对象p1’
cout << (int*)&p << endl;
//程序运行结束,释放拷贝的对象p1',调用析构函数
}
int main()
{
//test01();
//test02();
test03();
system("pause");
return 0;
}
运行结果:
- Person 默认构造函数调用
- 005DF904
- Person 拷贝构造函数调用
- Person 析构函数调用
- 005DF9FC
- Person 析构函数调用
- 请按任意键继续. . .
5.5 构造函数调用规则
5.5.1 构造函数调用规则
① 默认情况下,C++编译器至少给一个类添加3个函数。
1. 默认构造函数(无参,函数体为空)
2. 默认析构函数(无参,函数体为空)
3. 默认拷贝构造函数,对属性进行值拷贝
② 构造函数调用规则如下:
1. 如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造。
2. 如果用户定义拷贝函数,C++不会再提供其他构造函数。
③ 巧记法,如下图所示,如果定义中间的,上面的就默认不定义了,下面的默认定义。
5.5.2 调用定义的拷贝构造函数
#include <iostream>
using namespace std;
//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)
class Person
{
public:
Person()
{
cout << "Person 默认构造函数调用" << endl;
}
Person(int age)
{
m_Age = age;
cout << "Person 有参构造函数调用" << endl;
}
Person(const Person & p)
{
m_Age = p.m_Age;
cout << "Person 拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 析构函数调用" << endl;
}
int m_Age;
};
void test01()
{
Person p;
p.m_Age = 18;
Person p2(p);
cout << "p2的年龄:" << p2.m_Age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
- Person 默认构造函数调用
- Person 拷贝构造函数调用
- p2的年龄:18
- Person 析构函数调用
- Person 析构函数调用
- 请按任意键继续. . .
5.5.3 调用默认的拷贝构造函数
#include <iostream>
using namespace std;
//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)
class Person
{
public:
Person()
{
cout << "Person 默认构造函数调用" << endl;
}
Person(int age)
{
m_Age = age;
cout << "Person 有参构造函数调用" << endl;
}
//编译器自动提高拷贝构造函数
~Person()
{
cout << "Person 析构函数调用" << endl;
}
int m_Age;
};
void test01()
{
Person p;
p.m_Age = 18;
Person p2(p); //调用编译器默认的拷贝构造函数会把p的所有属性拷贝过来
cout << "p2的年龄:" << p2.m_Age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
- Person 默认构造函数调用
- p2的年龄:18
- Person 析构函数调用
- Person 析构函数调用
- 请按任意键继续. . .
5.5.4 调用定义的有参构造函数
#include <iostream>
using namespace std;
//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)
class Person
{
public:
Person(int age)
{
m_Age = age;
cout << "Person 有参构造函数调用" << endl;
}
//编译器自动提高拷贝构造函数
~Person()
{
cout << "Person 析构函数调用" << endl;
}
int m_Age;
};
void test02()
{
Person p; //如果写了有参构造函数,编译器就不再提供默认构造,依然提供拷贝构造构造
//由于没有默认构造函数,所以报错
Person p2(p);
}
int main()
{
test02();
system("pause");
return 0;
}
5.5.5 调用定义的拷贝构造函数
#include <iostream>
using namespace std;
//构造函数的调用规则
//1、创建一个类,C+=编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)
class Person
{
public:
//如果写了拷贝构造函数,编译器就不再提供其他普通构造函数
Person(const Person& p)
{
m_Age = p.m_Age;
cout << "Person 拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 析构函数调用" << endl;
}
int m_Age;
};
void test01()
{
Person p; //没有默认构造函数,报错
Person(10); //没有有参构造函数,报错
Person p2(p);
cout << "p2的年龄:" << p2.m_Age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}