再探类和对象 C++

1. 构造函数

1.1 概念

在我们学习c语言版的数据结构的时候,我们经常会调用初始化函数来初始化我们自定义的数据结构,这未免有点麻烦,而且有时候会忘记调用,造成未知的后果,那能否在对象创建的时候就把信息设置进去呢?

构造函数就应运而生,构造函数是一个特殊的成员函数,名字和类名相同,创建类对象时有编译器自动调用,以保证类的成员有合适的初始值,并且这个在整个对象的生命周期只调用一次。

1.2 特性

可能大家会被这个函数的名字误导成是用来创造对象的,但该函数的作用是用来初始化变量的。

该函数的特性:

  1. 函数名和类名相同。
  2. 没有返回值(不是void就是字面意思的没有)。
  3. 和普通函数一样支持重载
  4. 由编译器自动调用
  5. 在整个对象的生命周期只调用一次。
  6. 可以缺省
class Date
{
public:
   //无参构造
   Date()
   {
   	_year = 2023;
   	_month = 10;
   	_day = 23;
   }
   //带参函数
   Date(int year, int month, int day)
   {
   	_year = year;
   	_month = month;
   	_day = day;
   }
   //缺省
   Date(int year, int month = 10, int day = 23)
   {
   	_year = year;
   	_month = month;
   	_day = day;
   }
private:
   int _year;
   int _month;
   int _day;
};

注:全缺省和无参构造函数不同同时存在,调用的时候会产生歧义,不知道调用哪个

好,先在我们知道如何定义一个构造函数,那我们如何调用呢?

class Date
{
public:
	//无参构造
	Date()
	{
		_year = 2023;
		_month = 10;
		_day = 23;
	}
	//带参函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date date;//调用无参构造
	Date date1(2024,10,23);//调用有参构造
	date.Print();
	date1.Print();
	return 0;
}

在这里插入图片描述
注:大家是不是认为调用无参构造的时候应该写成Date date(),看起来很有道理,但是大家注意这是不是和函数的声明矛盾了?所以这样写是不合理的。

  1. 如果没有显示的定义构造函数,编译器会默认自动生成一个不带参数的构造函数,但如果自己写了,则不会生成。
class Date
{
public:
	
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date date;
	date.Print();
	return 0;
}

在这里插入图片描述

现在显示定义一个构造函数

class Date
{
public:
	Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date date;
	date.Print();
	return 0;
}

在这里插入图片描述

看完这点以后大家可能会疑惑,编译器调用默认的构造函数之后,我们看打印的数据还是随机值,这是否意味这这个默认的构造函数没什么用?首先说答案,肯定是有用的。

在解释之前我们先了解一个概念:C++把数据分为两类,一类是内置类型,例如int、char、指针…,还有一类就是自定义类型,例如class、struct、union…,默认的构造函数对内置类型是没什么用的,但是对于自定义类型,默认的构造函数会使自定义类型调用它默认的构造函数。

class Time
{
public:
	Time()
	{
		_hour = 22;
		_minute = 55;
		_second = 30;
	}
	void Print()
	{
		cout << _hour << "-" << _minute << "-" << _second << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//内置类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	Time time;
};
int main()
{
	Date date;
	date.time.Print();
	return 0;
}

在这里插入图片描述

注:这里我们嘴中说的默认的构造函数,不仅仅指的是编译器自动生成的构造函数,我们写的无参构造函数和全缺省参数构造函数,也是默认的构造函数,也就是可以不传参数就调用构造,都可以叫默认构造。

2. 析构函数

2.1 概念

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

2.2 特性

  1. 析构函数是在类名前面加上~
  2. 没返回类型,和构造函数一样,而且还没有参数
  3. 一个类只能有一个析构函数,不能重载
  4. 和构造函数一样,如果你不显示的定义析构函数,编译器会默认的生成一个析构函数。
  5. 在对象的生命周期结束时,编译器会自动的调用析构函数完成资源回收
  6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack、Queue类

7.如果在一个自定义类型A里面,还有其他的自定类型B,A自定义类型生命周期结束的时候,会默认的调用B的析构函数。

class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
	void Print()
	{
		cout << _hour << "-" << _minute << "-" << _second << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	~Date()
	{
		cout << "~Date()" << endl;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	int _year;
	int _month;
	int _day;
	Time time;
};
int main()
{
	Date date;
	return 0;
}

在这里插入图片描述

3. 拷贝构造函数

3.1 概念

身为资深CV工程师,我们在创造一个对象的时候,能否创造一个和已存在的对象一样的对象呢?
拷贝构造函数闻着味就来了,它只有一个形参,而且必须是对本类对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用

3.2 特性

  1. 拷贝构造函数也是构造函数的一个重载。
  2. 拷贝构造函数,只有一个形参,而且必须是本类对象的引用形式,如果你使用传值,编译器会直接报错,因为会引发无穷的递归。
class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		_year = 2023;
		_month = 10;
		 _day = 24;
	}
	/*错误写法,因为值传递,也会调用构造拷贝,所以这里会无穷递归
	Date(Date d)
	{
		cout << "Date(Date&)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	*/
	//正确写法
	Date(Date& d)
	{
		cout << "Date(Date&)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date date1;
	Date date2(date1);
	date2.Print();
	return 0;
}
  1. 如果没有显示的写拷贝构造函数,编译器会默认的生成一个,默认生成的拷贝构造按内存存储的字节来拷贝,也就是浅拷贝或者叫值拷贝。
class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		_year = 2023;
		_month = 10;
		 _day = 24;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date date1;
	Date date2(date1);
	date2.Print();
	return 0;
}

在这里插入图片描述
注:如果类的成员里边有自定义类型,编译器会默认调用它的拷贝构造函数,内置类型则是按照字节直接拷贝而成的。

  1. 如果类中有资源申请(例如malloc),我们必须显示的定义拷贝,不能使用编译器默认的拷贝函数,如果没有,就可以不用写拷贝构造函数。
/*错误写法*/
class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		_year = 2023;
		_month = 10;
		 _day = 24;
		 arr = (int*)malloc(sizeof(int) * 4);
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day;
	}
	~Date()
	{
		cout << "~Date()" << endl;
		free(arr);
	}
private:
	int _year;
	int _month;
	int _day;
	int* arr;
};

int main()
{
	Date date1;
	Date date2(date1);
	return 0;
}

在这里插入图片描述
上面这段代码是错误的示范,因为上面的代码使用的是编译器默认生成的拷贝构造函数,完成的是浅拷贝,也就是两个对象的指针成员指向的是一块空间,根据上面我们所学的析构函数可知,当对象的生命周期结束,编译器会自动调用析构函数,完成资源回收,所以上面这段代码对一块空间free了两次(根据运行结果也能看出),所以错了。

/*正确写法*/
class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
		_year = 2023;
		_month = 10;
		 _day = 24;
		 _arr = (int*)malloc(sizeof(int) * 4);
	}
	Date(Date& d)
	{
		_arr = (int*)malloc(sizeof(int) * 4);
		memcpy(_arr, d._arr, sizeof(int) * 4);
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day;
	}
	~Date()
	{
		cout << "~Date()" << endl;
		free(_arr);
	}
private:
	int _year;
	int _month;
	int _day;
	int* _arr;
};

int main()
{
	Date date1;
	Date date2(date1);
	return 0;
}

在这里插入图片描述

  1. 拷贝构造函数的典型应用
    (1)使用已存在对象创建新对象
    (2)函数参数类型为类类型对象,因为如果传值的话会发生拷贝,导致效率下降
    (3)函数返回值类型为类类型对象,因为如果返回值的话,也会发生拷贝,导致效率下降。
    总结:为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值