⭐本篇为C++学习第6章,主要了解默认成员函数,构造函数,析构函数
⭐本人Gitee C++代码仓库:yzc的c++学习: 小川c++的学习记录 - Gitee.com
目录
一. 类的6个默认成员函数
二. 构造函数
三.析构函数
五. 下章重点
5.1 拷贝构造函数
5.2 运算符重载和赋值运算符重载
一. 类的6个默认成员函数
对于一个空类来说,其大小是1,但是空类中真的什么都没有吗??
其实,任何类在什么都不写时候,编译器会默认生成6个成员函数
1. 完成类对象的初始化和清理工作:
构造函数用于完成对象的初始化
析构函数用于完成对象的清理
2. 完成类对象的拷贝和赋值:
拷贝构造用于完成类初始化时候使用一个对象拷贝另一个对象
赋值运算符重载用于使用一个对象赋值给另一个对象
3. 取地址重载:
普通对象和const对象的取地址重载,我们很少自己实现
注意:
1. 默认生成的构造函数和析构函数。针对成员变量:内置类型不会处理,但是成员变量是其他类的话,会调用这个类的构造函数。
2.默认生成的拷贝构造和赋值运算符重载。只会按字节序拷贝(浅拷贝)
也就是说,很多类不需要我们去实现这些函数,编译器会自动生成。
但是对于拷贝构造和赋值运算符重载。由于C++默认生成的是浅拷贝,这会造成错误。如:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;class A
{
public:A(int a = 0){_a = &a;}~A(){free(_a);}
private:int* _a;
};int main()
{A a;A b(a);A c(2);a = c;return 0;
}
由于默认的浅拷贝,对象a,b,c中的_a指向同一块空间。
当调用析构函数的时候,这块空间会被析构多次,就会报错。
所以,我们需要自己实现拷贝函数和赋值运算符重载的深拷贝,以避免出现这种问题。
二. 构造函数
我们以时间类为例,逐步讲解。
我们知道,在C语言可以在结构体中实现一个Init函数,用于初始化结构体
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;class Time
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){cout << _year << "/" << _month << "/" << _day;}
private:int _year;int _month;int _day;
};int main()
{Time t1;t1.Init(2024, 9, 24);//完成初始化工作t1.print();return 0;
}
但是这种方法有点麻烦,我们能不能在创建对象的时候就初始化呢?
这个时候就需要使用构造函数来完成这个功能
构造函数的名称和类名是一样的!
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;class Date
{
public://无参的构造函数Date(){_year = 0;_month = 0;_day = 0;}//有参的构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date t1;Date t2(2024, 9, 24);t1.print();t2.print();return 0;
}
运行结果:
使用构造函数,我们能够方便地对对象进行初始化
对于有参和无参构造函数,我们还能使用缺省值的特点来合并。
//带缺省值的构造函数,合并了有参构造和无参构造Date(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}
注:
1. 构造函数(在对象初始化调用的函数) ---> 完成类的初始化工作,
a 函数名和类名相同,无返回值
b 对象初始化的时候自动调用类中的构造函数
c 构造函数可以重载,完成不同对象的初始化(有参和无参)
d 如果类中没有自定义构造函数,编译器会默认生成一个无参的构造函数
e 如果用户自定义了一个有参的构造函数,那么系统不会自动生成,这个时候需要用户自己定义一个无参的默认构造函数,
默认构造函数针对内置类型成员变量没有初始化,针对自定义类成员变量,会调用其的构造函数进行初始化,如:
class A { public:A(int a = 0){_a = a;}private:int _a; };class B {int _b;A a; };
在实际应用中,我们更倾向于使用全缺省的默认构造函数
f 无参的构造函数和全缺省的构造函数统称为默认构造函数,但是默认构造函数只能存在一个,所以这两个函数只能定义一个,两个同时存在会造成歧义
三.析构函数
析构函数用于完成对象的清理工作。当对象的生命周期结束后,就会自动调用。
class Date
{
public://带缺省值的构造函数,合并了有参构造和无参构造Date(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}//析构函数,由于没有使用new,malloc等开辟空间。函数内部为空~Date(){}void print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
注:
析构函数(用于完成对象的清理工作)
a 析构函数无返回值,无参数。
b 析构函数名字的在类名前加~
c 一个类只有一个析构函数,若用户没有自定义,编译器会默认生成一个
d 当对象的生命周期结束后,会自动调用其析构函数
e 和构造函数类似,默认生成的构造函数不会处理内置类型,对于类成员变量,会调用该类的析构函数
f 析构函数完成的是对象的清理,不是对象的销毁
g 注意析构的顺序,后创建的对象先析构
四 多个对象调用构造函数和析构函数的顺序
#include<iostream>
using namespace std;class A
{
public:A(){cout << "调用A构造函数A()" << endl;}~A(){cout << "调用A析构函数~A()" << endl;}
};class B
{
public:B(){cout << "调用B构造函数B()" << endl;}~B(){cout << "调用B析构函数~B()" << endl;}A a;
};int main()
{A a1;B b1;A a2;B b2;return 0;
}
分析一下,上面的的代码输出是什么??
应该是:
调用A构造函数A() //a1
调用A构造函数A() //b1
调用B构造函数B() //b1
调用A构造函数A() //a2
调用A构造函数A() //b2
调用B构造函数B() //b2
调用B析构函数~B() //b2(后创建,先析构)
调用A析构函数~A() //b2
调用A析构函数~A() //a2
调用B析构函数~B() //b1
调用A析构函数~A() //b1
调用A析构函数~A() //a1
运行结果如下