前言
这期将会讲解类的六个默认成员函数的前3个,后面的将会在类和对象(4)进行讲解
类的六个默认成员函数
这六个默认构造函数如果我们自己不写,那么编译器会自己生成–因为容易忘,所以搞的自动生成 默认成员函数不要搞成全局的,不然编译器还是会自己生成一个
1.构造函数–初始化 2.析构函数–清理 3.拷贝构造函数–用同类对象去初始化创建对象 4.拷贝赋值运算符–赋值 5和6.取地址及const取地址操作符重载
构造函数
作用:初始化对象
特征:(构造函数有时要搞成私有的,大多数情况下要设置成公有的)
1.函数名和类名相同
2.没有返回值(但也不需要写void)
3.对象实例化时编译器自动调用对应的构造函数
4.构造函数可以重载
class Date
{
public:
// 1.无参构造函数
Date()
{}
// 2.带参构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
调用构造函数的方法:
Date d1;
// 调用无参构造函数,对象后面不用加括号,加括号就和函数声明格式冲突了,编译器分辨不了
Date d2(2025, 1, 1);
// 调用带参的构造函数
系统自动生成的构造函数的规则:
内置类型不做处理(有些编译器会处理),自定义类型会去调用他的默认构造
1.内置类型/基本类型的概念:语言本身自带的类型,如:int/char/double/指针等等
2.自动类型:用struct/class等定义的类型
一般情况下,里面有内置类型成员,就要自己写构造函数;如果全部都是自定义类型成员或者内置类型成员都有缺省值并且初始化符合我们的预期,可以考虑让编译器自己生成
为了弥补这个内置函数这里的设置缺陷,C++11 又打了补丁——内置类型成员变量在类中声明时可以给默认值。(和全缺省构造函数区分)
默认构造函数的分类:(默认构造函数只能有一个,因为无参和全缺省的不要同时存在)
1.无参构造函数 2.全缺省构造函数 3.系统生成的构造函数
总结:不传参就可以调用的就是默认构造函数
全缺省构造函数举例: Data(int year = 2025,int month = 5,int day = 1) { _year = year; _month = month; _day = day; } TreeNode(int val = 0) { _left = nullptr; _right = nullptr; _val = val; }
关于全缺省函数碰上变量声明时给了默认值的情况:则要看具体反馈(自己不要这么做)
析构函数
作用:清理对象中的资源(也就是销毁对象),在对象生命周期结束时自动调用,一次只能析构一个对象
析构是按照构造的相反顺序进行析构(除非有东西的生命周期变了)
但是,要data d1这样出来的才行,如果是new出来的话,就要delete才行了
特性:
1.析构函数名时在类名前加上字符~
2.无参数无返回值(也不用void)
3.一个类只能用一个析构函数,没有显示定义的话,编译器会自动生成默认的析构函数
4.析构函数不能重载
5.对象生命周期结束时,C++编译系统会自动调用析构函数
多久要自己写析构函数,多久不用(常见的几种–来灵活点,不单单这几种嗷):
1.一般情况下,有动态申请资源,就需要显式写析构函数来释放资源
2.没有动态申请资源,不需要自己写析构
3.需要释放资源但是成员都是自定义类型,不需要写析构
一般来说存在堆上的资源(动态申请就存在堆上的)才需要手动释放
注意:同一块空间不能被连续析构两次及以上,这跟动态内存管理有些不同
调用成员函数结束之后也会调用一次析构函数
拷贝构造函数
概念:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特征:1.拷贝构造函数是构造函数的一个重载形式。(也是一种构造函数)
2.拷贝构造函数的参数只有一个,而且这个参数必须是类类型对象的引用;如果用传值方式的话编译器会直接报错,因为这种方式会引发无穷递归调用 3.如果没有自己显式定义,那么系统会生成一个默认拷贝构造函数
类类型对象:指由类定义的具体对象
关于第二点为什么会调用无穷递归的解释:(编译器通过报错阻止了其发生)
调用无穷递归的原因: eg: class Date() { public: Date(const Date data)//这里一直是自定义类型传参,所以就一直在调用拷贝构造函数,无穷无尽 //这里的传参就相当于Date data(d1) { _year = data.year;//这里就相当于this->_year = d._year; } private: int _year; } main函数里有: Date d1; Date d2(d1);//这个的意思是用d1去初始化d2,this指向的是d2 引申:自定义类型的传参调用的拷贝构造函数,在给Date(这个接受传参)的函数结束之后才会调用析构
正确的做法: 类里面的那个成员函数应该改成: Date(const Date& d) {}//引用的话,只是让d绑定到d1上,注意不能用指针
C++规定:在传参时
1.内置类型直接拷贝(无须调用拷贝构造函数)
2.自定义类型必须调用拷贝构造函数来完成拷贝(如果形参需要创建新对象的话)
默认拷贝构造函数的规则:
1.内置类型成员完成浅拷贝(浅拷贝也叫做值拷贝)
2.自定义类型成员会调用他的拷贝构造函数
注意:拷贝构造函数的形参那里建立加上const,跟上面一样
总结:对象传参时,形参必须使用引用类型(规定了的);返回时的话能用引用就用引用(注意引用的那个东西会不会出去之后就不见了),返回时如果不用引用,那还要拷贝一次
多久用的是构造函数,多久用的是拷贝构造函数:
构造函数:用于创建新对象(初始化新内存)
拷贝构造函数:用于从已有对象复制创建一个新对象
深拷贝和浅拷贝
概念:
浅拷贝:对于指针,只复制指针地址,不复制指针指向的内容
(会出现多个对象用的是同一块内存的情况)
深拷贝:对于指针,会复制指针指向的内容给一块新空间
对那些基本数据类型,这两个都是直接复制值
多久用深拷贝,多久用浅拷贝:
涉及到资源申请的,就必须要用深拷贝(不然后面会同一空间多次调用析构函数)
没涉及到的就用浅拷贝就行了