【C++心愿便利店】No.5---构造函数和析构函数

这篇具有很好参考价值的文章主要介绍了【C++心愿便利店】No.5---构造函数和析构函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言

👧个人主页:@小沈YO.
😚小编介绍:欢迎来到我的乱七八糟小星球🌝
📋专栏:C++ 心愿便利店
🔑本章内容:类和对象
记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~


提示:以下是本篇文章正文内容,下面案例可供参考

一、类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数
【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言

二、构造函数

如下Date类,没有初始化打印出来就会是随机值,同时对于栈没有初始化,就会报错
那如果想能否在创建对象的同时,就将信息设置进去呢。因此,就有了构造函数。以Date类为例:

#include<iostream>
using namespace std;
class Date
{
public:
	void Init(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 d1;
	d1.Print();//没有调用Init初始化函数
	Date d2;
	d2.Init(2022, 7, 6);//调用Init初始化函数
	d2.Print();
	return 0;
}

【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?

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

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象
其特征如下:

  • 🌏函数名与类名相同。
  • 🌏无返回值。(不需要写void)
  • 🌏对象实例化时编译器自动调用对应的构造函数。

对于上述代码所运行后的结果没有初始化d1结果是随机值,然后对比下述代码(同样没有初始化d1)及结果;运行结果自动初始化为1/1/1不是随机值且打印了Date(),这就说明对象实例化时编译器自动调用对应的构造函数

#include<iostream>
using namespace std;
class Date
{
public:
	构造函数
	Date()
	函数名与类名相同
	且无返回值
	{
		cout << "Date()" << endl;
		_year = 1;
		_month = 1;
		_day = 1;
	}
	void Init(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 d1;
	d1.Print();
	return 0;
}

【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言

  • 🌏构造函数可以重载。(本质可以写多个构造函数,提供多种初始化方式)
#include<iostream>
using namespace std;
class Date
{
public:
	1. 无参的构造函数
	Date()
	{}
	也可以写成下面这种
	Date()//函数名与类名相同且无返回值
	{
		cout << "Date()" << endl;
		_year = 1;
		_month = 1;
		_day = 1;
	}
	2. 有参的构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	3. 全缺省的构造函数
	Date(int year=1, int month=1, int day=1)
	无参和全缺省的不能同时存在会存在调用歧义
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Init(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 d1;调用无参的构造函数
	//Date func();
	这也可以是一个函数声明所以为了区分不能加()
	d1.Print();
	Date d2(2023,8,28);调用带参的构造函数
	d2.Print();

	对于全缺省的构造函数使用更灵活可以传一个参数,两个等
	Date d3(2023);
	d3.Print();
	Date d4(2023, 8);
	d4.Print();
	return 0;
}

🌟注意:如果通过无参构造函数创建对象时,对象后面不用跟括号(例如Date d1() 是错误的 ),否则就成了函数声明以下代码的函数:声明了d1函数,该函数无参,返回一个日期类型的对象warning C4930: “Date d1(void)”: 未调用原型函数(是否是有意用变量定义的?)
【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言

  • 🌏如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
 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 d1;
 return 0;
 }

将Date类中构造函数注释后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数;将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用

  • 🌏不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??

🌟解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,所有类型的指针都是内置类型

#include<iostream>
using namespace std;
class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言
🌟注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值

#include<iostream>
using namespace std;
class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	
	这个地方不是初始化而是声明,声明给的缺省值,默认生成的构造函数就会用这个缺省值初始化
	int _year = 2023;
	int _month = 9;
	int _day = 5;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
是默认构造函数。它们都有一个共同的特点:可以不用传参。默认构造函数只能有一个,前面两个,在语法上可以构成函数重载,但是在无参调用的时候,会发生歧义,出现调用不明确。

注意:要把默认构造函数和默认成员函数区分清楚,默认成员函数是我们不写编译器会自动生成的,默认构造函数是不需要传参的构造函数。编译器生成的构造函数,既是默认构造函数,同时也是默认成员函数。
🌟为什么上述说内置类型用的缺省值

#include<iostream>
using namespace std;
class Date
{
public:
	Date()
	{
		这里_year没有给值而_month _day给了值打印出来是2023/2/1所以声明那给的是缺省值
		
		_month = 2;
		_day = 1;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year = 2023;
	int _month = 9;
	int _day= 5;
};

int main()
{

	Date d1;
	d1.Print();
	return 0;
}

【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言

🌟编译器生成的默认构造的特点:

  1. 不写才会生成,写了任意一个构造函数就不会生成了
  2. 内置类型也叫基本类型(语言用关键字定义的类型);内置类型的成员不会处理(根据编译器有的会有的不会)
  3. 自定义类型的成员才会处理,会去调用这个成员的构造函数

🌟总结:
一般情况下,都需要我们自己写构造函数,决定初始化方式;成员变量全是自定义类型,可以考虑不写构造函数

  • 🌏无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个多个并存在会存在调用的二义性。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数(不传参就可以默认调用的)

三、析构函数

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

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

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

温馨提示:析构函数不能重载。
🌟后定义先析构

#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	~Date()
	{
		cout << "Date()" << endl;
	}
private:
	int  _year;
	int _month;
	int _day;
};
class Stack
{
public:
	Stack(size_t n=4)
	{
		cout << "Stack(size_t n=4)" << endl;
		if (n == 0)
		{
			a = nullptr;
			top = capacity = 0;
		}
		else
		{
			a = (int*)malloc(sizeof(int) * n);
			if (a == nullptr)
			{
				perror("realloc fail");
				exit(-1);
			}
			top = 0;
			capacity = n;
		}
	}
	void Init()
	{
		a = nullptr;
		top = capacity = 0;
	}
	void Push(int x)
	{
		if (top == capacity)
		{
			size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
			int*tmp = (int*)realloc(a,sizeof(int) * newcapacity);
			if (tmp == nullptr)
			{
				perror("realloc fail");
				exit(-1);
			}
			if (tmp == a)
			{
				cout << capacity << "原地扩容" << endl;
			}
			else
			{
				cout << capacity << "异地扩容" << endl;
			}
			a = tmp;
			capacity = newcapacity;
		}
		a[top++] = x;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(a);
		a = nullptr;
		top = capacity = 0;
	}
	int Top()
	{
		return a[top - 1];
	}
	void Pop()
	{
		assert(top > 0);
		--top;
	}
	void Destroy()
	{
		free(a);
		a = nullptr;
		top = capacity = 0;
	}
	bool Empty()
	{
		return top == 0;
	}
private:
	int* a;
	int top;
	int capacity;
};
int main()
{
	Date d1;

	Stack st1;
	Stack st2;//后定义的先析构
	return 0;
}

【C++心愿便利店】No.5---构造函数和析构函数,C++ 心愿便利店,c++,开发语言
Stack中的成员变量a、capacity、top都是内置类型,对象st1生命周期结束要销毁的时候,a和capacity和top是在栈上不需要资源清理,最后由系统将其内存回收,而a指向的空间是在堆区上申请的,这块空间不会随着对象生命周期的结束而自动释放,所以会造成内存泄漏,因此在对象销毁前,要通过析构函数去释放成员变量a指向的空间,这就是析构函数的作用。

  • 🌏 关于编译器自动生成的析构函数,是否会完成一些事情呢?

下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数

#include<iostream>
using namespace std;
class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

程序运行结束后输出:~Time()
在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数;如果类中没有申请资源时(在堆上申请空间),析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date(日期)类;有资源申请时,一定要写,否则会造成内存泄漏,比如Stack类文章来源地址https://www.toymoban.com/news/detail-704443.html


到了这里,关于【C++心愿便利店】No.5---构造函数和析构函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++心愿便利店】No.9---C++之内存管理

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:内存管理 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 【说明】 栈 又叫堆栈–非静态局部变量/函数参数/返回值

    2024年02月08日
    浏览(46)
  • 【C++心愿便利店】No.7---C++之运算符重载

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:运算符重载 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 通常比较两个操作数的大小,会写成下述方式: 但是对

    2024年02月08日
    浏览(46)
  • 【C++心愿便利店】No.1---C++入门(命名空间、缺省参数)

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:命名空间、缺省参数 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ C语言最初由 Dennis Ritchie 于 1969 年到 1973 年在 ATT 贝尔实验室里开发出来,主要用于重新实现 Unix 操作

    2024年02月11日
    浏览(40)
  • 【C++心愿便利店】No.4---C++初谈类和对象

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:类和对象 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 C语言是面向过程的,关注的是过程,分析出求解问题的步

    2024年02月10日
    浏览(38)
  • 【C++心愿便利店】No.11---C++之string语法指南

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:string 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 C语言中,字符串是以’\\0’结尾的一些字符的集合,为了操作方

    2024年02月05日
    浏览(52)
  • 【C++心愿便利店】No.14---C++之探索list底层原理

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:list 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 list的文档介绍 list是可以在常数范围内在任意位置进行插入和删

    2024年02月05日
    浏览(58)
  • 【C++心愿便利店】No.13---C++之探索vector底层原理

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:vector 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 STL(standard template libaray-标准模板库):是C++标准库的重要组成部

    2024年02月05日
    浏览(48)
  • 【C++心愿便利店】No.8---C++之重识类和对象

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:再谈构造函数、Static成员、友元、内部类、匿名对象、再次理解封装 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 在创建对象时,编译器通过调用构造函数,给对象中

    2024年02月08日
    浏览(42)
  • 社区便利店选址五要素,真实案例分析便利店选址步骤分享!

    顺为城市地图:www.data-dance.com  近年来,各地政府陆续出台了具体的政策指标,以推动城乡社区便利店的建设,随着中国城镇化率和城市人口密度逐年提升,也将带动便利店经济的发展。 数据显示,2021年中国便利店行业市场规模,达 2988亿元 ,门店数量达 15.7万家 。 中国不

    2024年02月05日
    浏览(52)
  • 基于SSM的便利店系统

    ✌全网粉丝20W+,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅 文末获取项目下载方式 🍅 一、项目背景介绍: 基于SSH的便利店系统是为了满足顾客、店长和管理者

    2024年02月08日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包