类和对象【中】

一:类的六大默认成员函数

  • 上一次我们认识了如何创建一个类,这次我们再来看看类里面还有些什么
  • 只要我们创建了一个类,即使我们不编写,编译器也会代替我们生成六个函数,所以我们把这六个函数称为类的六大默认成员函数
  1. 构造函数:用于初始化对象
  2. 析构函数:用于清理对象(比如delete释放申请的空间)
  3. 拷贝构造函数:用同类对象来初始化对象
  4. 赋值操作符重载:将一个对象赋值给另一个对象
  5. 取地址操作符重载:取得对象的地址
  6. const对象的取地址操作符重载:取得const对象的地址

二:构造函数

  • 我们之前在使用C语言创建一个结构体的时候,比如链表,想要初始化它都需要写一个初始化函数,像这样:
    请添加图片描述
  • 这样每次我们创建一个对象就要调用一次初始化函数,好麻烦啊,能不能两步并一步呢?
  • 当然可以,构造函数应运而生,它能够在创建一个对象的时候自动就把它初始化了
  • 它有以下几个特点
  1. 函数名与类名相同
  2. 无返回值(不是返回void,是直接不写返回值类型)
  3. 对象实例化的时候编译器自动调用对应的构造函数
  4. 构造函数可以重载(保证了多种初始化方式,拷贝构造其实就是构造函数的一个重载)
  • 光说不练假把式,来看看它和DateInit函数有什么区别
    请添加图片描述
  • 当我们构造了一个无参或者全缺省的构造函数(上图就是全缺省的)时,我们不传参数时是不需要加()的,会被编译器当成一个函数的声明
    请添加图片描述
  • 构造函数可以有很多,但是推荐使用全缺省的,不传参的时候可以用默认参数,想要指定数值也可以传参
    请添加图片描述
  • 之前说过,我们不写构造函数,编译器会自动生成一个,那么我们为什么要写呢?
  • 因为编译器自动生成的构造函数对内置类型什么都不做
    请添加图片描述
  • 可以看到d1中的值还是随机值,但是它又不是完全没做事,对于内置类型,它会调用它的构造函数
    请添加图片描述
  • 可以看到Date类中有个我们自己创建的类型Tmp,我们用Date类实例化一个对象时Tmp中的a被修改成1了,说明编译器自动生成的构造函数会调用自定义类型的构造函数
  • 还有一点,可以看到图中所有的成员变量前面都对了个_,这是为了在初始化时便于区分
    请添加图片描述
  • 我们可以不在成员变量前加_或者其它符号做区分,但不推荐
  • 最后一点,如何理解默认构造函数?
  • 我常误以为编译器自动生成的就是默认构造函数,这么说没错,但是不全面
  • 默认构造函数其实有三种
  1. 我们不写,编译器自动生成的
  2. 我们自己写的无参构造函数
  3. 我们自己写的全缺省的构造函数
    请添加图片描述

三:析构函数

  • 和构造函数设计的初衷相似,我们之前经常使用完一个自定义的结构体后,还需要调用destroy函数
  • 而有了析构函数,它能完成和destroy函数相同的功能,但是我们不需要自己去显示的调用它,当对象被销毁时编译器会自动调用析构函数
  • 它有如下的特点:
  1. 函数名为类名前加上~
  2. 无参数无返回值
  3. 一个类有且只有一个析构函数(无法重载,没参数当然无法重载啦)
  4. 对象生命周期结束时,C++会自动调用析构函数
    请添加图片描述
  • 可以看到,我们没有显示调用析构函数,编译器还是调用了,我们打印一下证明它被调用了
  • 编译器默认生成的析构函数和构造函数相同,对内置类型不做处理,对自定义类型会调用其析构函数
    请添加图片描述

四:拷贝构造函数

  • 拷贝构造函数其实就是构造函数的一种重载,它的参数是类类型的引用
  • 特点:
  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用
  3. 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
  • 我们来看看拷贝构造函数的实现
    请添加图片描述
  • 如果不传引用,那么会发生无限循环中,因为这时函数传参时就要调用拷贝构造了
    请添加图片描述
  • 这个函数通常情况下我们是可以不写的,它和构造和析构不同,编译器自动生成的拷贝构造它不区别对待了,对于内置类型它也会干事了
  • 对内置类型它会直接拷贝,对于自定义类型调用它的拷贝构造函数
    请添加图片描述
  • 可以看到我们没有写拷贝构造函数,但是还是完成了拷贝构造,这就是使用的编译器默认生成的拷贝构造
  • 那么编译器生成的够用了,我们还要自己写吗?
  • 答案是大多数情况是不用的,但是对于某些类我们直接使用类自己生成的拷贝构造会出问题
    请添加图片描述
  • 拷贝构造s2时是值拷贝,说明s2的_a和s1的_a指向的是同一块空间,之后调用析构函数时_a指向的空间会被free两次,就出问题了
  • 所以,有需要的时候我们还是需要自己写拷贝构造函数

五:赋值操作符的重载

  • 在说赋值操作符的重载之前,我们需要先了解一下运算符重载
  • 运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
  • 函数名为:关键字operator后面接需要重载的运算符符号。
  • 函数原型:返回值类型 operator操作符(参数列表)
  • 注意:
  1. 作为类成员的重载函数时,其形参看起来比操作数数目少1,第一个形参为this指针,被编译器限定,我们虽然看不到它,但是实际存在
    • 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载
  2. 运算符重载和函数重载虽然都使用了重载一词,但无关联。函数重载是为了支持定义同名函数,运算符重载是为了让自定义类型可以像内置类型一样使用
  • 前置知识就说到这里,现在来举个例子,比如我们都知道两个类无法直接用==比较是否相等
  • 但是现在我们重载这个==操作符就可以实现
    请添加图片描述
  • 我们使用的时候只用使用==就可以了
  • 实际上编译器是这么调用的operator==(&d1,d2)
  • 函数真正其实长这样bool operator==(const Date*const this,const Date&d)
  • 言归正传,现在来讲讲赋值操作符的重载
  • 赋值操作符的重载也是实现的拷贝行为,但是和拷贝构造不同的是,拷贝构造是创建一个对象时,拿同类对象初始化的拷贝
  • 而赋值操作符的拷贝是对象已经存在,初始化完成了,想把一个对象的值赋值给另一个对象
  • 以日期类举例,函数原型大概长这样
  • Date& operator=(Date*const this,const Date&d)
  • 返回类型为Date&是为了支持一行连续赋值,this之前没加const是因为变量要被修改了,不能加const
Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	}
  • 注意:
  1. 返回类型为类类型的引用
  2. 检查是否给自己赋值
  3. 返回*this
    请添加图片描述
  • 与拷贝构造函数同理,我们不写编译器也会自动生成,并且对内置类型直接拷贝,对自定义类型调用其自己的赋值运算符重载
    请添加图片描述
  • 可以看到我们没有重载=但是还是完成了拷贝
  • 与拷贝构造一样,我们大多数时候不用自己写,但是遇到如stack这样的类,我们还是需要自己手动重载=的

六:const成员函数

  • 将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
  • void Print() const->void Print(const Date* const this)
  • 相当于const修饰*this,this指向的对象不能被修改
    请添加图片描述
  • const对象和非const对象都能调用const成员函数
    请添加图片描述
  • const对象无法调用非const成员函数
    请添加图片描述

七: 取地址和const取地址操作符的重载

  • 这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容
  • 说白了几乎不需要我们自己写,去掉这俩把默认成员函数当4个我觉得问题也不大
Date* operator&()
	{
		return this;
	}

const Date* operator&()const
{
	return this;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dhdw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值