C++ 类和对象(二) 构造和析构
文章目录
• 生活中买电子产品都会有出厂设置,在某一天我们不用的时候会删除自己的信息保证安全
• C++中的面向对象来源于生活,类似的每个对象也会有初始设置以及对象销毁前的清理数据设置
1. 构造函数和析构函数
构造: 初始化
析构: 清理
一个变量或者对象没有初始化,对其使用后果是未知的
同样,使用完一个对象或者变量,没有及时清理,也会有一定的安全问题
C++中使用构造函数和析构函数来解决上面的问题。
这两个函数会被编译器自动调用,完成初始化对象和清理的工作。
对象的初始化和清理工作是编译器强制要求我们做的事情,因此,就算我们没有写构造析构,编译器会提供他们自己的构造和析构函数,只不过这两个函数的函数体是空的。
1.1 构造和析构的作用:
• 构造函数:主要用于在创建对象时候为成员对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
• 析构函数:主要用在对象销毁前系统自动调用,执行一些清理工作,释放内存。
1.2 构造函数语法:
类名(可以有参数)
{
}
- 没有返回值,没有返回类型,也不写void
- 函数名称和类名一样
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无需手动调用,而且只调用一次(每次手机开机不需要进行初始化设置)
1.3 析构函数语法:
~类名(不能有参数)
{
}
- 没有返回值,不写void
- 函数名和类名一样,在名称前要加上符号 ~
- 析构函数不能有参数,不能发生重载
- 程序在对象销毁前会自动调用析构函数,无需手动调用,只调用一次
示例:
#include <iostream>
using namespace std;
class Person {
public:
//构造函数
Person() {
cout << "调用构造函数" << endl;
}
//析构函数
~Person() {
cout << "调用析构函数" << endl;
}
};
void func()
{
Person p; //函数在栈区,调用完自动释放
}
int main()
{
func();
system("pause");
return 0;
}
2. 函数的分类和调用
2.1 两种分类方式
构造函数发生重载后的分类:
- 按参数分类: 有参构造和无参(默认构造)构造
- 按类型分类: 普通构造和拷贝构造(除拷贝构造之外的其他的都叫普通构造)
拷贝构造函数:
复制一个一摸一样的Person对象出来,
就要把传进来的对象的属性都复制给当前对象
传进来的对象不能更改 , 所以加了const
固定格式:
Person(const Person &p)
- 相同类型的对象是通过拷贝构造函数来实现复制的
- 函数名称必须跟本类相同,且是参数是本类的引用常量
2.2 三种调用方式
- 括号法
- 显示法
- 隐式转换法
注意:
- 调用默认构造函数时候,不要加()
因为会把它当作函数声明的,而不会创建对象了
int func(); //这是一个函数声明 返回类型 + 函数名 + 参数
Person p(); // 类似的 Person 是返回类型, p 是函数名 - 不要用拷贝构造函数来初始化一个匿名对象
编译器认为 Person(p3) 等价于 Person p3; 而前面p3 已经声明过了
示例:
#include <iostream>
using namespace std;
class Person {
public:
//构造函数
Person() {
cout << "---1---调用无参构造函数" << endl;
}
Person(int a) {
m_age = a;
cout << "---2---调用有参构造函数" << endl;
}
Person(const Person& p)
{
m_age = p.m_age;
cout << "---3---调用拷贝构造函数" << endl;
}
//析构函数
~Person() {
cout << "调用析构函数" << endl;
}
int m_age;
};
void func()
{
//括号调用
//Person p; //默认构造
//Person p1(10);//有参构造
//Person p2(p1); //拷贝构造
//显示调用
Person p3 = Person(10);
//Person p4 = Person(p3);
//Person(10);//匿名对象,执行完当前行就自动销毁
//cout << "aaaaaaaaaaaaa" << endl;
//不要用拷贝函数初始化一个匿名对象,编译器和i认为是对象的声明
//Person(p3);
//隐式调用
//Person p5 = 10; // Person p5 = Person(10)
//Person p6 = p5;
}
int main()
{
func();
system("pause");
return 0;
}
3. 拷贝构造函
构造函数的调用时机
C++中拷贝构造函数调用时机的三种情况:
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给参数传值
- 以值方式返回局部对象
//1. 使用一个已经创建完毕的对象来初始化一个新对象
void func()
{
Person p1(10);
Person p2(p1);
}
// 2. 值传递
void func0(Person p)
{
}
void func1()
{
Person p3(10);
func0(p3);
}
//3. 值方式返回对象
Person func2()
{
Person p(10);
cout << (int*)&p << endl;
return p;
}
int main()
{
cout << "-----使用已经创建完毕的对象来初始化对象" << endl;
func();
cout << "-----使用值传递的方式来初始化对象" << endl;
func1();
cout << "-----使用值方式返回对象" << endl;
Person p = func2();
cout << (int*)&p << endl;
system("pause");
return 0;
}
4. 构造函数调用规则
默认情况下,C++编译器至少给一个类添加3个函数
- 默认构造函数(无参, 函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数, 对属性进行值拷贝
构造函数调用规则:
• 如果用户定义有参构造函数, C++不再提供默认无参构造,但是会提供默认拷贝构造
• 如果用户定义了拷贝构造函数, C++不会再提供其他构造函数
提供下面的就不提供上面的了
示例:
#include <iostream>
using namespace std;
class Person {
public:
//Person() {
// cout << "调用默认构造函数" << endl;
//}
//Person(int a) {
// m_age = a;
// cout << "调用有参构造函数" << endl;
//}
Person(const Person& p)
{
m_age = p.m_age;
cout << "调用拷贝构造函数" << endl;
}
//析构函数
~Person() {
cout << "调用析构函数" << endl;
}
int m_age;
};
void func()
{
Person p1(20);
//p1.m_age = 20;
Person p2(p1);
cout << "p2的年龄是: " << p2.m_age << endl;
}
int main()
{
func();
system("pause");
return 0;
}
5. 深拷贝和浅拷贝
浅拷贝: 简单的赋值拷贝操作(编译器提供的)
问题是: 堆区的内存重复释放
深拷贝: 在堆区重新申请空间,进行拷贝操作
堆区的内存已经释放干净了,再释放就是非法操作
解决办法: 堆区的存放数据是一样的,但是指向堆区的位置不一样
注意点: 如果有属性是在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
示例:
class Person {
public:
Person() {
cout << "调用默认构造函数" << endl;
}
Person(int a, int height) {
m_age = a;
m_height = new int(height);//在堆区开辟了一段内存存放数据
cout << "调用有参构造函数" << endl;
}
Person(const Person& p)
{
m_age = p.m_age;
//m_height = p.m_height;//编译器的默认拷贝函数, 简单的值拷贝
m_height = new int(*p.m_height);//深拷贝
cout << "调用拷贝构造函数" << endl;
}
//析构函数
~Person() {
//将堆区开辟的数据做释放操作
//删除内存,安全
if (m_height != NULL)
{
delete m_height;
m_height = NULL;//防止野指针出现,置空
}
cout << "调用析构函数" << endl;
}
int m_age;
int* m_height;
};
void func()
{
Person p1(10, 180);
cout << "---p1的年龄是: " << p1.m_age << "身高是: " << *p1.m_height << endl;
Person p2(p1);
cout << "---p2的年龄是: " << p2.m_age << "身高是: " << *p2.m_height << endl;
}
int main()
{
func();
system("pause");
return 0;
}
规则1 : 用户定义了有参构造函数,C++编译器就不会提供默认构造函数了
但是会提供默认拷贝构造函数,用于值拷贝
规则2: 用户定义了拷贝构造函数,C++编译器就不再提供默认构造函数和有参构造函数