1.类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下 6 个默认成员 函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
2.构造函数
构造函数 是一个 特殊 的 成员函数 , 名字与类名相同 , 创建类类型对象时由编译器自动调用 ,以保证
每个数据成员都有 一个合适的初始值,并且 在对象整个生命周期内只调用一次 。
class Date
{
public:void Init(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 d1;d1.Init(2022, 7, 5);d1.Print();return 0;
}
对于 Date 类,可以通过 Init 公有方法给对象设置日期,但是每次都调用函数初始化太麻烦了,这里我们介绍使用构造函数。
特性
构造函数 是 特殊 的成员函数,不能以常规的情况理解,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是 初始化对象 。
其特征如下:
1. 函数名与类名相同 。
2. 无返回值。 不需要写(void)
3. 对象实例化时编译器 自动调用 对应的构造函数。
4. 构造函数可以 重载 。
5. 如果类中没有显式定义构造函数,则 C++ 编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
6. C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即: 内置类型成员变量在 类中声明时可以给默认值。
7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
构造函数的使用
1.无参,自动调用
#include<iostream>
#include<assert.h>
using namespace std;class Date
{
public:Date(){cout << "Date()" << endl;_year = 1; _month = 1;_day = 1;}void Print(){//cout << this << endl;cout << _year << "/" << _month << "/" << _day << endl;}private:int _year; // 声明int _month;int _day;
};
int main()
{Date d1; d1是一个对象,调用无参构造函数,无参数调用时不能加()d1.Print();return 0;
}
对象实例化时编译器自动调用对应的构造函数。无参数调用时不能加()
2.含参数调用,自动调用
#include<iostream>
#include<assert.h>
using namespace std;class Date
{
public:Date(int year, int month, int day) 也可以写形参,传参调用{_year = year;_month = month;_day = day;}void Print(){//cout << this << endl;cout << _year << "/" << _month << "/" << _day << endl;}private:int _year; // 声明int _month;int _day;
};
int main()
{Date d2(2023, 7, 20); d2是一个对象含参数调用构造函数 必须这样写,规定就是参数写在对象的后面d2.Print();return 0;
}
含参数调用构造函数 必须这样写,d2是一个对象,规定就是参数写在对象的后面
d1对象使用构造函数和d2对象使用构造函数对比
3.全缺省形式的构造函数
#include<iostream>
#include<assert.h>
using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1)//把上面两种形式的构造函数合并//写成全缺省函数{_year = year;_month = month;_day = day;}void Print(){//cout << this << endl;cout << _year << "/" << _month << "/" << _day << endl;}private:int _year; // 声明int _month;int _day;
};
int main()
{Date d1;d1.Print();Date d2(2023, 7, 20);//必须这样写,规定就是参数写在对象的后面d2.Print();Date d3(2023);d3.Print();Date d4(2023, 7);d4.Print();return 0;
}
4.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
不定义构造函数,编译器是否自动生成?
class Date
{
public:void Print(){//cout << this << endl;cout << _year << "/" << _month << "/" << _day << endl;}~Date(){cout << "~Date()" << endl;}private:int _year; //= 1; // 声明给的缺省值int _month; //= 1;int _day; //= 1;
};int main()
{// 构造函数,也是默认成员函数,我们不写,编译器会自动生成Date d1;d1.Print();return 0;
}
生成了随机值,使用了编译器自动生成的构造函数。
5.关于编译器生成的默认成员函数 || 默认构造函数
不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用?
内置类型和自定义类型:
解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,指针等等,自定义类型就是我们使用class/struct/union等自己定义的类型。
看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员调用的它的默认成员函数。
class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}
6.C++11 中针对内置类型成员,内置类型成员变量在类中声明时可以给默认值。
当没有自定义构造函数时,如果在内置类型成员声明中给了缺省值,调用编译器自动生成的构造函数时会把声明给的缺省值当成默认值使用,而不是给随机值。
class Date
{
public:void Print(){//cout << this << endl;cout << _year << "/" << _month << "/" << _day << endl;}~Date(){cout << "~Date()" << endl;}private:int _year = 1; 声明给的缺省值int _month = 1;int _day = 1;
};int main()
{构造函数,也是默认成员函数,我们不写,编译器会自动生成Date d1;d1.Print();return 0;
}
class Date
{
public:Date() 构造函数不全部内置类型的初始化,调用构造函数时会使用内置类型声明给的的缺省值做默认值{_month = 2;_day = 2;}void Print(){//cout << this << endl;cout << _year << "/" << _month << "/" << _day << endl;}~Date(){cout << "~Date()" << endl;}private:int _year = 1; 声明给的缺省值int _month = 1;int _day = 1;
};int main()
{构造函数,也是默认成员函数,我们不写,编译器会自动生成Date d1;d1.Print();return 0;
}
7.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。总结:不传参的就是默认构造函数。
3.析构函数
概念:
通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而 对象在销毁时会自动调用析构函数,完成对象中资源的清理工作 。
特性:
析构函数 是特殊的成员函数,其 特征 如下:
1. 析构函数名是在类名前加上字符 ~ 。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。
4. 对象生命周期结束时, C++ 编译系统系统自动调用析构函数。
5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如 Stack类。
自动调用析构函数示例
从执行结果和调试窗口都观察到编译器会自动调用析构函数销毁申请的资源
没有申请资源时,例如日期类,不需要写析构函数,没有可销毁的资源
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。