类和对象(中篇)

文章目录

1.类的6个默认成员函数

严格意义上来说一个类什么都没有,我们把它叫做空类;
比如:

class A
{
}

这就是一个空类,站在我们人类角度看,这个类确实没有什么都没有;
那么真的是什么都没有吗?
当然不是,注意我刚才的前提:站在人类的角度;
但是我们站在编译器的角度来看呢,这个类中其实有6个默认的成员函数!
这6个成员函数是由编译器自动生成的;
默认成员函数:用户没有显示实现,编译器会自动生成的成员函数我们称为默认成员函数,当然我们也可以自己显示实现,那么便能一起就不会生成对应的默认成员函数了,实现的默认成员函数则还是会由编译器自动实现!

按照这6个默认成员函数的功能,我们可以分为两两一组:
初始化和清理:构造函数和析构函数;
拷贝赋值: 拷贝构造函数和赋值运算符重载函数;
取地址重载: 主要是普通对象和const对象取地址,这两个很少会自己实现;

在这里插入图片描述

2.构造函数

2.1构造函数的引入

首先我们知道在C++语法中是支持在结构体中定义成员函数的!
那么现在我们可以简单的定义一个栈类来玩一玩:
在这里插入图片描述
由此一个简单的类就完成了,这里我们需要使用手动调用初始化函数类初始化这个栈,只有完成到这,我们才能在后续操作中完成进栈和压栈操作!
但是呢?Init函数是需要我们手动调用的,
而c++为了解决这一点创造了构造函数(初始化由编译器自动完成)
下面是两者的对比
在这里插入图片描述
很明显代码量变小了;更加符合我们懒人的标准了;

2.2构造函数的特性

上面我们讲了为什么会由构造函数,下面我们来讲述一下构造函数的定义和特性

2.2.1构造函数的定义:

1…类名+(参数1,参数2,参数3,……)
1.1不需要写返回类型(不是说返回类型是void的意思,而是真的没有返回值的意思,连viod也不需要)
在这里插入图片描述
1.2构造函数名和类名相同(一个对向在生命周期内只调用一次)

2.2.2.构造函数可以 实现函数重载**

上面我们定义了一个无参的构造函数,那么接着我们也可以定义有参的构造函数:
在这里插入图片描述

2.2.5.从函数的参数类型来区分构造函数

1.无参的构造函数:
在这里插入图片描述
2.有参的构造函数
在这里插入图片描述
由赋值的参数个数区分:
满参数赋值
在这里插入图片描述

不满参数赋值
在这里插入图片描述

2.2.3 构造函数调用(对象实例化时,由编译器自动调用对应的构造函数!)

无参函数调用————即为定义
在这里插入图片描述

有参函数调用
在这里插入图片描述
看到这里你会不会有一个疑问;为什么无参数调用没有括号;如果按照有参的样式的化,无参数函数应该是
在这里插入图片描述
原因:
如果我们从函数角度来看:Stack s1();这一调用方式,si是函数名,返回值是Stack类型!这不妥妥的函数声明吗?这会使便使编译器陷入歧途;

这里的无参数调用和全缺省调用构造函数的调用相同:(都会显示使用默认构造函数)**
在这里插入图片描述

都是无参数调用;
在这里插入图片描述
所以要求不能同时出现这两个函数;

建议构造函数时,使用全缺省函数;

2.2.4。如果类中没有显示定义构造函数,C++编译器会自动生成一个无参钩造函数

一旦用户显示定义,编译器就不会自动生了!
例子:
在这里插入图片描述
这里我们定义了一个有参的构造函数,如果我再去调用无参构造函数编译器就会报错;
在这里插入图片描述

2.2.5.使用全缺省构造函数来多元创建栈帧;

构造函数的定义09 .。。。

2.2.6.编译器自动生成的默认构造函数(无参),该构造函数对于对象中的内置类型成员变量不进行处理;对于自定义类型的成员函数则调用该自定义类型的默认构造函数(无参数,全缺省,自动);

在这里插入图片描述
内置类型:int,char等;注意char*和结构体指针也是内置类型

对于自定义类型自动初始化的优势,对栈建立的队列有优势;
在这里插入图片描述
自动生成

2.2.7C++11规定可以给声明缺省值

这里的缺省值可以将自动生成的构造函数初始化;

在这里插入图片描述

2.2.8默认构造函数(不传参数)的种类:

全缺省的构造函数
无参的构造函数
自动生成的构造函数
有且只有一个

3.析构函数

3.1概念

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

3.2 特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
    函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

显示写析构函数的样例:
栈:
在这里插入图片描述

3.3析构的调用顺序:

逆序调用;构造函数顺序调用;

3.4默认析构函数的处理

在这里插入图片描述
1.的原因:
// 内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对
象,所以在// d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数
内置成员在栈帧中,而栈是在堆中存储;内置成员会在栈帧销毁时自动销毁;
自己定义析构函数的目的时将堆的内存清理;

4.拷贝构造函数

出现的原因:
栈的拷贝在析构时,会进行多次析构堆;
在这里插入图片描述
解决方法一
-

4.1.拷贝构造函数概念

在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎
在这里插入图片描述
那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

拷贝构造函数: 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

4.2拷贝构造函数特性**

拷贝构造函数也是特殊的成员函数,其特征如

4.2.1、拷贝构造函数格式:

类名(const 类名& 形参名) or 类名(类名& 形参名)
注意:该只有参数是以上形式的构造函数才是拷贝构造函数!!!!!切记切记!!!不是这样格式的函数都不能叫做拷贝构造函数!!!!!!
这里注意:参数一定有引用 ;否者拷贝构造函数将下入死循环;(原因:c++的传参数是拷贝构造函数);
举个具体例子:
在这里插入图片描述
死循环的示例:
在这里插入图片描述

4.2.2拷贝构造函数上述的调用


这里的拷贝构造函数有常参数,和变参数;
调用拷贝构造含函数的用法:
1.括号法;
2.等号法;

4.2.3

在这里插入图片描述

4.3、拷贝构造函数的分类

若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。浅拷贝存在风险、一般情况像Stack类这种需要额外申请空间的类,就需要我们自己写拷贝构造函数,并且是用深拷贝来实现!
我们通过具体的例子来解释一下浅拷贝与深拷贝:
我们知道编译器提供的默认拷贝构造函数是由浅拷贝来实现的,我们就来用一用浅拷贝:
在这里插入图片描述

4.3.1浅拷贝:

普通的类型(c中的)或自定义中没有堆的使用

4.3.2深拷贝

自定义类型的呗(这一自定义类型有调用了堆的内存的)

4.3.3.拷贝构造函数的使用场景:

拷构造函数的使用场景是:
参数的传递;
返回值的传递
定义对象;另一个对象赋值;

5…赋值运算符重载(自定义类型)

5.1 运算符重载
5.1.1定义:

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)
目的:是可读性变强

5.1.2运算符重载和函数实现的对比

在这里插入图片描述
运算符重载的函数名:operator <
简写:<

5.1.3运算符重载的注意:
  • [ ]

1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须自定义类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this
5. .* :: sizeof ?: .注意以上5个运算符不能重载。这个经常在笔试选择题中出 现。

在这里插入图片描述

不能改变操作符造作数的个数;

// 全局的operator==
class Date
{ 
public:
 Date(int year = 1900, int month = 1, int day = 1)
   {
        _year = year;
        _month = month;
        _day = day;
   }    
//private:
 int _year;
 int _month;
 int _day;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
bool operator==(const Date& d1, const Date& d2)
{
    return d1._year == d2._year
   && d1._month == d2._month
        && d1._day == d2._day;
}
void Test ()
{
    Date d1(2018, 9, 26);
    Date d2(2018, 9, 27);
    cout<<(d1 == d2)<<endl;
}

终结
定义:返回值+operator+操作符+(参数)
参数要少一个;原因:this指针;

5.2 赋值运算符重载
5.2.1 定义加注意:

在这里插入图片描述

5.2.2赋值重载的特性:

1.会自动生成(自定义类型需要写)即:
浅拷贝不用写;直接(用类名加()或=)
深拷贝要写;函数;
例如:
时间类:浅拷贝;
栈类:深拷贝;

5.3运算符重载
5.3.1 +=
Date& Date :: operator+=(int day)
{
	_day += day;
    while (_day> GetMonthDay(_year,_month))
	{
	
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
}
5.3.2+
// 日期+天数

Date Date :: operator+(int day)
{
	//Date tmp(*this);
	//tmp += day;
	//return tmp;
	Date d = *this += day;
	return d;
}

对比+符用+=来完成定义;

 /*日期+=天数*/

Date& Date :: operator+=(int day)
{ 
	_day += day;
    while (_day> GetMonthDay(_year,_month))
	{
	
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
	//* this = *this + day;
	//return *this;
}



// 日期+天数

Date Date :: operator+(int day)
{
	Date tmp(*this);
	tmp += day;
	return tmp;
	Date d = *this += day;
	return d;
	//方法二就是将this指针的解引用改为其他变量;
	/*Date tmp(*this);
	tmp._day += day;
	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	{

		tmp._day -= GetMonthDay(tmp._year, tmp._month);
		++tmp._month;
		if (tmp._month == 13)
		{
			++tmp._year;
			tmp._month = 1;
		}
	}
	return *this;*/

对比+=符用+来完成定义;

Date& Date :: operator+=(int day)
{ 
	/*_day += day;
    while (_day> GetMonthDay(_year,_month))
	{
	
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;*/
	* this = *this + day;
	return *this;
}



// 日期+天数

Date Date :: operator+(int day)
{
	//Date tmp(*this);
	//tmp += day;
	//return tmp;
	//Date d = *this += day;
	//return d;
	//方法二就是将this指针的解引用改为其他变量;
	Date tmp(*this);
	tmp._day += day;
	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	{

		tmp._day -= GetMonthDay(tmp._year, tmp._month);
		++tmp._month;
		if (tmp._month == 13)
		{
			++tmp._year;
			tmp._month = 1;
		}
	}
	return *this;
}

对比两者(拷贝次数)
在这里插入图片描述
发现第一种最好;

5.3.3 -=
// 日期-=天数

Date& Date ::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)//没有0号
	{
		_month--;
		
		if (_month ==0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);

	}
	return *this;
} 
5.3.4 -
Date Date ::operator-(int day)
{
	Date d = *this -= day;
	return d;
}

day为-数时的解决方法
在这里插入图片描述
修改后的+=

Date& Date :: operator+=(int day)
{ 
	//day为负数时
	if (day < 0)
	{
		return *this -= (-day);
	}
	_day += day;
    while (_day> GetMonthDay(_year,_month))
	{
	
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
	//* this = *this + day;
	//return *this;
}

修改后的-=

Date& Date ::operator-=(int day)
{
	//day为负数时
	if (day < 0)
	{
		return *this += (-day);
	}
	_day -= day;
	while (_day <= 0)//没有0号
	{
		_month--;
		
		if (_month ==0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);

	}
	return *this;
} 

5.3.5 前置++和后置++

两个++的定义:
在这里插入图片描述

// 前置++

Date& Date ::operator++()
{
	*this += 1;
	return *this;

}


// 后置++

Date Date :: operator++(int)
{
	Date d = *this;
	*this += 1;
	return d;
}

后置++在参数中添加了一个整数(可以是任意值 )

注意这两个函数构成函数重载运算符重载
在这里插入图片描述
前置好一些

5.3.5 前置–和后置–
// 前置--

Date& Date ::operator--()
{
	*this -= 1;
	return *this;
}
// 后置--

Date Date ::operator --(int)
{
	Date tmp(*this);
	*this -= 1;
	return tmp;
}

后置的显示:

在这里插入图片描述

5.3.6日期减日期

方法1:
在这里插入图片描述
方法2:用小的逐个加;

 /*日期-日期 返回天数*/

int Date :: operator-(const Date& d)
{
	//方法2
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (*this<d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)//为什么不用<操作过多;
	{
		++min;
		++n;
	}
	return n * flag;
}
使用两个栈构建的队列所有默认成员函数都不用写;

6.const成员

在类型前加const‘类型后加对象名字;
const成员只能调用const成员函数权限的频移
普通成员函数可以调用const成员函数权限的缩小
当一个对象只想实现读;就最好写成const
原因:这样只想读的对象和即想读又想写的对象都可调用;

6.1this指针是const类型的函数在这里插入图片描述

这里的this指针由于不可显示;所以,C++就将函数的声明和定义后面加const
定义:
在这里插入图片描述
声明:
在这里插入图片描述
注意:区别const在函数返回类型前和人在函数后加const的区别;
在这里插入图片描述
示例
在这里插入图片描述
“【】”重定义的注意:
在这里插入图片描述

6.2const函数的应用

适用于对象即可读又可写 的的对象;所以
1.对象只读(对于s1)

2.即读又写
在这里插入图片描述
由于两者兼备所以写了重定义函数;
解决方法:
在这里插入图片描述

注意
后面的const是给对象加的
-和+都可加;+=和-=不可以
在这里插入图片描述
反之则会有问题;

7.取地址及const取地址操作符重载

这俩个函数平常不用写;是默认成员函数,自动调用。
在这里插入图片描述
使用场景
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值