前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++的六大“天选之子“之“构造“与“析构“

C++的六大“天选之子“之“构造“与“析构“

作者头像
初阶牛
发布2023-10-14 17:52:36
1520
发布2023-10-14 17:52:36
举报
文章被收录于专栏:C语言基础

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏1: 🍔🍟🌯C语言初阶 🐻推荐专栏2: 🍔🍟🌯C语言进阶 🔑个人信条: 🌵知行合一 🍉本篇简介:>:讲解C++中有关类和对象的介绍,本篇是中篇的第一篇文章. 金句分享: ✨别在最好的年纪,辜负了最好的自己.✨

一、“构造函数"与"析构函数”

1.1 “构造函数”

不知道友友们有没有过这样一段经历. 在写一道数据结构的oj题时,信心满满的提交后,发现,编译居然编译不过,找了半天发现是忘记了进行初始化操作. 很多时候我们经常忘记初始化操作,但是初始化操作每次又是必做的,那么C++的祖师爷(本贾尼大佬)就贴心的给我设计了一个函数,这个函数就是构造函数.

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

(1) 自动生成的"构造函数"

构造函数编译器会自动调用,那我们不写构造函数会怎样呢?

下面这段代码会报错吗?

代码语言:javascript
复制
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Print();
	Date d2;
	d2.Print();
	return 0;
}

答案:

并不会报错,因为构造函数即使如果在类中没有显式定义,编译器也会自动生成一个默认的析构函数

运行结果:

-858993460–858993460–858993460 -858993460–858993460–858993460

但是.构造函数对于内置类型(例如:int ,double,指针等等)并不做处理,对于自定义类型(结构体,类,联合,枚举等等),会调用自定义类型自己的构造函数.

代码语言:javascript
复制
#include <iostream>
using std::cin;
using std::cout;
using std::endl;

class Time
{
public:
	Time()//Time类的默认构造函数
	{
		cout << "Time的构造函数" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
	void Print()
	{
		cout << _hour << "-" << _minute << "-" << _second << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		_time.Print();//打印时间
	}
private:
	int _year;
	int _month;
	int _day;
	Time _time;
};

int main()
{
	Date d1;
	d1.Print();
	Date d2;
	d2.Print();
	return 0;
}

运行结果:

Time的构造函数 -858993460–858993460–858993460 0-0-0 Time的构造函数 -858993460–858993460–858993460 0-0-0

解释: 虽然Date类我们没有写构造函数,但是编译器自动生成了一个隐藏的构造函数并且对自定义类型(这里是Time类)会调用自己的构造函数,所以Time类中的成员已经初始化了.

(2) 自定义"构造函数"

前面提到,内置类型,默认构造函数是不进行处理的,而这显然是设计的不合理的,所以在C++11中,打了个补丁,内置类型成员变量在类中声明时可以给默认值(缺省值),记住这里是默认值,并不是真的存储变量,因为声明是没有空间存储变量的.类只是图纸,并不能住人,只有实例化成对象后,才可以住人.

示例:下面这段代码并没有显示定义构造函数.

代码语言:javascript
复制
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year=2020;//类中只是声明,并不存储变量,这里给出的是缺省值(很重要)
	int _month=10;
	int _day=1;
};

int main()
{
	Date d1;
	d1.Print();
	printf("\n");
	Date d2;
	d2.Print();
	return 0;
}

运行结果:

2020-10-1 2020-10-1

常见的三种默认构造函数:

  1. 无参构造函数
  2. 全缺省构造函数
  3. 带参构造函数

当然,学过函数重载的友友 们一定知道全缺省和无参会存在调用不明确的情况.

代码语言:javascript
复制
class Date
{
public:
	Date()//无参构造函数
	{
		_year = 2020;
		_month = 1;
		_day = 1;
	}
	Date(int year=2020,int month=1,int day=1)//全缺省构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	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;
};

运行结果:

总结:
  1. 构造函数的函数名与类名相同。无返回值(不是void)。
  2. 对象实例化时编译器自动调用对应的构造函数。
  3. 构造函数可以重载。(为了解决满足多样的初始化要求)
  4. 默认构造函数是对内置类型不进行处理,C++11中,打了个补丁,内置类型成员变量在类中声明时可以给默认值(缺省值)
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成,所以无参,全缺省,默认构造函数只能有一个.

1.2 “析构函数”

同样,我们设计构造函数的目的是帮助我们自动调用初始化操作,因为初始化操作经常忘记且又是必须的.那析构函数呢? 析构函数是很多时候我们经常忘记销毁操作,如果是动态申请的空间,很容易导致内存泄漏,那么我们亲爱的C++的祖师爷(本贾尼大佬)就又给我们设计了一个函数,这个函数就是析构函数.

析构函数:

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

构造函数名与类名相同,那析构函数呢? 析构函数名是在类名前加上字符 ~。

示例:

代码语言:javascript
复制
#include <iostream>
using std::cin;
using std::cout;
using std::endl;

typedef int DataType;
class Stack
{
public:
	Stack(int capacity=5)//全缺省构造函数
	{
		cout << "Stack" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)//压栈操作
	{
		CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()//析构函数
	{
		cout << "~Stack"<< endl;
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity *
				sizeof(DataType));
			if (temp == NULL)
			{
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
int main()
{
	Stack s;
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);
	return 0;
}

运行结果:

Stack ~Stack

我们不难发现,即使我们不写函数调用,构造函数和析构函数都会被自动调用.不愧是祖师爷的亲儿子,两个函数都有特权.

总结:
  1. 析构函数名是类名前加上字符 ~。无参数无返回值类型。
  2. 与构造函数不同的是,析构函数不支持函数重载,所以一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  3. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  4. 编译器生成的默认析构函数对自定义类型会调用它自己的析构函数,析构函数对于内置类型,不会有任何影响,因为内置类型的对象没有需要释放的资源。
  5. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。但是呢,牛牛建议还是都写吧,否则忘记写就可能发生内存泄漏,反正不难.

本篇文章只提到了"构造"与"析构"函数这两个天选之子,还有四个没有登场哦!敬请期待吧!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、“构造函数"与"析构函数”
    • 1.1 “构造函数”
      • (1) 自动生成的"构造函数"
      • (2) 自定义"构造函数"
      • 总结:
    • 1.2 “析构函数”
      • 总结:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档