类的对象(中)

目录

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

2.构造函数 

特征

3.析构函数

特性

构造和析构一些题

4.拷贝构造函数

特征

5.赋值运算符重载


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

2.构造函数 

构造函数是一个 特殊的成员函数 ,名字与类名相同,创建类类型对象时由编译器自动调用,以保证
每个数据成员都有 一个合适的初始值 ,并且在对象整个生命周期内只调用一次。
构造函数的特点是 初始化数据

特征

1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载。
class Date
 {
  public:
      // 1.无参构造函数
      Date()
     {}
  
      // 2.带参构造函数
      Date(int year, int month, int day)
     {
          _year = year;
比特就业课
5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。
          _month = month;
          _day = day;
     }
  private:
      int _year;
      int _month;
      int _day;
 };
  
  void TestDate()
 {
      Date d1; // 调用无参构造函数
      Date d2(2015, 1, 1); // 调用带参的构造函数
     Date d3();声明了d3函数,该函数无参,返回一个日期类型的对象
注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明

5.

 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦

用户显式定义编译器将不再生成。 

6.

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类

型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型

在C++中,默认构造函数对内置类型(如int,char等)类型不做处理,对自定义类型调用自定义类型的构造函数。这也是我们看到随机值的原因。


注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。 

3.析构函数

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

特性

1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
函数不能重载
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
5.与构造函数一样,对于内置类型,编译器自动调用的析构函数也不处理。对于自定义类型,则会调用它的默认析构。
class PEO
{
public:
	~PEO()
	{
		cout << "析构函数:~PEO()" << endl;
	}
	int _age;//年龄
	int _height;//身高
	int* _tmp;
};
class STU
{
public:
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
	PEO peo;
};
int main()
{
	STU stu1;
	return 0;
}

注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。


总结一句话就是,析构函数和构造函数一样,对内置类型不做处理,对自定义类型调用相应的析构函数。 

构造和析构一些题

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

对象析构要在生存作用域结束的时候才进行析构

在函数F中,本地变量a和b的构造函数(constructor)和析构函数(destructor)的调用顺序是: ( )

Class A;

Class B;

void F() {

	A a;

  B b;

  }

先构造,a,b构造,然后析构,b,a析构

2.

注意static对象的存在, 因为static改变了对象的生存作用域,需要等待程序结束时才会析构释放对象

全局对象先于局部对象进行构造

设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?( )

C c;

int main()

{

	A a;

	B b;

	static D d;

  return 0;

}

构造的顺序为c a b d 

析构的顺序为 b a d  c (static修饰的变量会放在局部对象之后进行析构)

4.拷贝构造函数

构造函数是创建对象时的初始化,而拷贝构造函数可以理解为拷贝一个对象初始化的值。

特征

1. 拷贝构造函数是 构造函数的一个重载形式。
2. 拷贝构造函数的 参数只有一个 必须是类类型对象的引用 ,使用 传值方式编译器直接报错
因为会引发无穷递归调用。
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void show()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 1, 1);
	Date d2(d1);
	d1.show();
	d2.show();
	return 0;
}

创建d1对象后,想拷贝一份给d2,因为d1是自定义类型,调用拷贝构造函数Date,d是d1的别名,直接访问成员变量赋值给d2的成员变量。

 

思考一下,直接传值为什么不行?

Date(const Date d)//不传引用会出现什么情况
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

 如果不加引用,直接传值的话,会出现无限递归拷贝的情况。

为什么传值就需要拷贝构造呢?以前写的很多函数都有传值传参啊。

这是因为内置类型(int、char)编译器很熟悉如何拷贝,需要拷贝多少字节。

但是这里是自定义类型,有很多难以直接拷贝的类型(如:链表、树),所以需要用到拷贝构造。

每一次实参传值给形参,都需要拷贝构造一份实参再继续下去,就形成了无限递归,因此编译器禁止这种行为。

用引用则不会产生这种情况,因为引用d相当于是d1的别名,直接操纵d1的成员变量。

此外,在引用前加const是为了防止前后位置写反(d._year = _year的情况)。这样编译器能检查出来。

需要写析构函数~Stack( )的,需要深拷贝;

不需要写析构函数~Stack( )的,编译器默认的浅拷贝即可。

5.赋值运算符重载

 对于内置类型,运算符可以直接使用(如:int a = 1,b = 2; a+b;)

如果是自定义类型想要直接使用运算符,就需要用到运算符重载(与函数重载没有关系)

一个日期类要写相等应该怎么写呢?

#include<iostream>
using namespace std;
 
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
 
 
int main()
{
	Date d1(2023,10,17);
	Date d2(2023,10,17);
 
	return 0;
}

判断是否相等,返回值类型可以用bool,函数名是operator后的运算符,参数必须是类类型的。

(bool如果相等返回值是1,如果不相等返回值是0)

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator == (const Date& d)
	{
		return _year == d._year &&
			_month == d._month &&
			_day == d._day;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2023, 10, 17);
	Date d2(2023, 10, 17);
	cout << (d1 == d2) << endl;
	return 0;
}

显示函数的参数多了,我们传参传两个,用两个参数接收怎么会多呢?————因为有隐含的this指针。这里正因为有这个隐含的this指针作了一个参数,所以会显示参数过多。

因此,我们拿掉一个参数,就传一个,this指针相当于d1,d就是d2的别名

这里传参要引用传参,因为传值传参会多一个拷贝构造(自定义类型默认),并且比较两个对象不做修改,用const修饰更好。

大于的写法

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

bool operator>(const Date& d)
{
	if (_year > d._year)
		return true;
	else if (_year == d._year && _month > d._month)
		return true;
	else if (_year == d._year && _month == d._month && _day > d._day)
		return true;
	else
		return false;
}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2023, 10, 3);
	Date d2(2023, 10, 2);
	

	cout << (d1 > d2) << endl;
	return 0;
}

注意:这里有一种简便写法,以大于等于>=为例,既然上面已经分别写好了等于和大于的重载,那么大于等于可以直接复用:

	bool operator>=(const Date& d)
	{
		return *this > d || *this == d;
	}

比较的运算符重载完成后,我们来看一下 " +=" 是如何操作的。

首先写一个能得到月份天数的函数

class Date
{
public:
	int GetMonthday(int year, int month)
	{
		static int monthday[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
			return 29;
		else
			return monthday[month];
	}

实现+=的日期

Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthday(_year,_month))
		{
			_day -= GetMonthday(_year, _month);
			_month++;
			if (_month > 12)
			{
				_year++;
				_month -= 12;
			}
		}
		return *this;
	}

注意这里是引用返回,因为我们写的是 += ,会改变日期,如果不用引用返回,*this出作用域之前会先拷贝,用引用返回就省了拷贝。

那不改变日期的显然就是 " + " 了,那么就要用到拷贝构造,不需要引用返回了。

实现+的日期

	Date operator+(int day)
	{
		Date ret(*this);
		ret += day;
		return ret;
	}
 
 int main()
{
	Date d2(2022,1,2);
	d2 + 1000;
 
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值