简单易懂---类和对象(中)

目录

类的6个默认成员函数

 1.构造函数

1.特性

 2.为什么不初始化?

总结:

 2.析构函数

1.特性

1.1什么时候自己写构造函数?

2.面试题

 3.拷贝构造函数

1.特征

 问:那么为什么传值会引发无穷调用?

 问:那么除了拷贝构造,其他函数还需要用引用作为形参吗?

2.什么情况下我们需要写拷贝构造,什么情况下不需要呢?

4.赋值操作符重载 

1.运算符重载

特性

函数名字:

函数原型:

2.赋值操作运算符

const成员函数

为什么要用Date&作为返回值?

总结:

区别拷贝构造和赋值重载


类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中什么都没有吗?并不是的,任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数

51db274255b04d14a8f9c4c18c9e6d82.png

 1.构造函数

特殊成员函数

构造函数主要用于对象的初始化

自己Init的缺陷,可能会忘记初始化

C++为了解决这个问题,引入构造函数

其名虽叫构造,但其主要任务并不是开空间创建对象,而是初始化对象

1.特性

  • 函数名与类名相同
  • 无返回值
  • 对象实例化时编译器自动调用对应的函数–保证对象一定会初始化
  • 构造函数可以重载 --你可以有多种初始化方式

class Date {
public:
	//1.无参构造函数
	Date() {

	}
	
	//2.全缺省构造函数
	Date(int year = 1, int month = 1, int day = 1) {
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};


void TestDate() {
	Date d1;//调用 无参/全缺省 构造函数
	Date d2(2022, 3, 24);//调用带参构造函数

	//Date d3();//带括号是函数声明,并不是调用

}

int main() {
	TestDate();
	return 0;
}
  • Date和全缺省的Date基本只能存在一个

  • 注意:如果类中没有显示定义构造函数,则 C++编译器会自动生成一个无参的默认构造函数,一旦用户显示定义编译器将不再自动生成.

    默认构造函数有三种:

    • 编译器默认生成的构造函数(对内置类型不会处理,还是随机值;对于自定义类型会去调用他们自己的构造函数初始化)
    • 全缺省构造函数
    • 无参构造函数
  • 但不会给你默认初始化,也就是说还是一堆随机值,没有初始化

 2.为什么不初始化?

  • 内置类型

语言原生定义的类型,如:int,char,double等,还有指针。

编译器对内置类型不进行初始化

  • 自定义类型

编译器会去调用它们默认构造函数去初始化。如:class\struct

前提是其中有自己写的构造函数,如:class中含有一个类,含有那个类如果有自写构造函数,就会初始化,没有就不会初始化

总结:

实际应用中突发情况很多,最好我们自己写一个全缺省的构造函数,能够应对大多数情况。


 2.析构函数

特殊成员函数

与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而
对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。

1.特性

  • 析构函数名是在类名前加上字符“~”
  • 无参数无返回值
  • 一个类只有一个析构函数,若未定义,系统会自动生成默认的析构函数
  • 对象生命周期结束时,系统自动调用析构函数
  • 编译器生成的默认析构函数,会对自定义类型成员调用它的析构函数
class A;
class B;
void func()
{
	A a;
	B b;
}

构造顺序是按照语句的顺序进行构造,析构是按照构造的相反顺序进行析构

因此  a构造 b构造 b析构 a析构

1.1什么时候自己写构造函数?

对象在栈上的栈帧开辟的空间会随着函数调用结束而释放掉,但是如果涉及到了资源的申请,比如堆上申请资源,那么就要求我们自己实现析构函数释放掉。

2.面试题

数据结构的栈和堆和我们讲的内存分段区域也有个栈和堆,它们之间有什么区别和联系

  • 它们之间没有绝对的联系,因为他们两个属于两个学科的各自的一些命名
  • 一个是数据结构,一个是一段内存分段
  • 数据结构的栈和系统分段的栈(函数中的栈帧)中的对象都符合先进后出

 3.拷贝构造函数

特殊成员函数

注意深浅拷贝问题

默认成员函数,我们不写编译器会自动生成拷贝构造

1.特征

  • 拷贝构造函数是构造函数的一个重载形式
  • 拷贝构造函数的参数只有一个必须使用引用传参,使用传值方式会引发无穷递归调用

 问:那么为什么传值会引发无穷调用?

103dc69472b540f787223cab8ecf742d.png

 当我们要传值给形参时,是需要创建然后拷贝过去的,这个时候需要调用拷贝构造。调用拷贝构造又需要传值给形参,传值给形参又需要拷贝构造,这样依次往下,就会引发无穷递归调用

所以说如果我们使用引用传参就可以很好就解决这个问题,因为引用就是别名,形参等于实参。

 问:那么除了拷贝构造,其他函数还需要用引用作为形参吗?

void f1(Date d1)
{
    
}

void f2(Date& d2)
{
    
}

我们调用f1,还需要调用拷贝构造,将我们实例化对象拷贝过去;

而引用传参调用f2,d2是其实例化别名,直接运用即可。

故引用传参可以大大提升效率

2.什么情况下我们需要写拷贝构造,什么情况下不需要呢?

当我们不写拷贝构造时,函数会自己生成一个默认拷贝构造(浅拷贝)
当类中有指针类型成员变量的时候,一定要自定义拷贝构造和赋值运算符

当类中只是日期类那些浅拷贝就能用的,我们不需要自己定义拷贝构造


默认生成的拷贝构造:

对内置类型会完成浅拷贝(值拷贝),也就是说,按内存存储按字节序完成拷贝

对自定义类型会调用他的拷贝构造完成拷贝


如果类中的内置类型有指针,这种情况下使用函数自己生成的默认构造函数会出问题吗?

  • 当然会有问题,浅拷贝就相当于让两个东西指向同一块内存空间,如果是指针,两个指针指向同一块空间,调析构函数会把同一块空间free两次,这样就会出现错误,一次malloc只能free一次。
  • 其中一个对象插入删除数据,都会导致另一个对象也插入删除数据

4.赋值操作符重载 

1.运算符重载


C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数

让自定义类型可以像内置类型意义使用运算符

具有返回值类型、函数名字以及参数列表 与普通函数类似

运算符重载跟函数重载没有关系

特性

  • 不能和通过连接其他符号来创建新的操作符,如:operator@
  • 重载操作符必须有一个类类型或者枚举类型的操作数
  • 用于内置类型的操作数,其含义不能改变
  • 如:内置类型整型+,其含义不能改变
  • 作为类成员的重载函数时,其形参看起来比操作数目少1成员函数的操作符有一个默认形参this,限定为第一个 形参
  • .*,::,sizeof,?:,.  五个操作符不能重载
class Date
{
public:
    bool operate==(Date& x)
    {
        return _year == x._year
            && _month == x._month
            && _day == x._day;
    }
    //编译器会将其处理为:
    //bool operate==(Date* this,Date& x);
private:
    int _year;
    int _month;
    int _day;
};

函数名字:

关键字operator后面接需要重载的运算符号

函数原型:

返回值类型operator操作符(参数列表)

2.赋值操作运算符

默认成员函数

不写函数会默认生成一个

// 赋值运算符重载
//用于两个已经存在的对象
// d2 = d3 -> d2.operator=(&d2, d3)

	Date& operator=(const Date& d)
	{
		if (*this != d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

 Date& operator=(Date& d) const -> Date& operator=(const Date& d)

这两句代码是等价的,效果是一模一样的


const成员函数

  • const对象不可以调用非const成员函数;
  • 非const对象可以调用const成员函数;
  • const成员函数内不可以调用其它的非const成员函数 
  • 非const成员函数内可以调用其它的const成员函数。

为什么要用Date&作为返回值?

  • 前面用void会发生错误,当使用d1=d2=d3这种连续赋值时,d1将不会被赋值,加上Date将解决这个问题。
  • 同时运用引用,避免其调用拷贝构造。同时这个时候函数结束,其作用域还在,可用引用做返回。

总结:

编译器默认生成赋值运算符跟拷贝构造的特性一致。

  • 针对内置类型,会完成浅拷贝,像Date这样的类不需要我们自己写赋值运算符
  • 针对自定义类型,编译器会调用它的赋值运算符重载

区别拷贝构造和赋值重载

  • 拷贝构造-----拿一个已经存在的对象去构造初始化另一个要创建的对象
  • 赋值重载----两个已经存在的对象 -> 拷贝

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值