C++类和对象-类的默认成员函数之构造函数与析构函数

前言:当一个类中什么成员都没有称为空类,那空类是类中什么都没有吗?事实上并非如此,当类中用户啥也没写时,编译器会自动生成6个默认的成员函数。
环境:VS2013

编译器自动生成以下6个成员函数。
在这里插入图片描述
目录
构造函数
析构函数

一、构造函数

#include<iostream>
using namespace std;
class Data
{
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()
{
	Data d1;
	d1.Init(2022, 11, 21);
	d1.Print();

	Data d2;
	d2.Init(2022, 11, 22);
	d2.Print();
	return 0;
}

我们通过自定义的公有方法给对象d1,d2进行初始化,但我们可以看到在每次创建对象时都得调用该方法进行信息设置,那我们可以想,能否在对象创建时就将信息设置在对象里呢?

  1. 定义
    特殊成员函数,函数名与类名相同,创建类类型对象时由编译器自动调用,在对象整个生命周期内只调用一次。
  2. 特性
    (1)函数名与类名相同
    (2)无返回值
    (3)创建对象时编译器自动调用
    (4)构造函数可重载
    Data日期类。
    Data() //无参构造函数
	{}
	
	Data(int year, int month, int day) //全缺省构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

(5)若类中用户没有显式定义构造函数,编译器会自动生成一无参的默认构造函数;若用户显式定义了,编译器便不再生成。
①用户显式定义时,调用用户显式定义的构造函数。

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

在这里插入图片描述
②用户未显式定义构造函数,编译器会自动生成一无参的默认构造函数。
在这里插入图片描述
我们可以看到当调用编译器自动生成的默认构造函数时,d2对象中各值为随机值。对象调用了编译器自动生成的默认构造函数,但对象中_year/_month/_day却为随机值,那是不是编译器生成的默认构造函数并没什么用呢?

事实上,C++语法规定,当用户未显式定义任何构造函数,编译器一定会生成一无参的构造函数;但编译器在具体实现时,可能会考虑到程序的运行效率问题,编译器感觉生成的构造函数并无多大意义时,编译器便不实现此方法。
这也是语法上和程序具体实现上的有所不同。

(6)那编译器生成的默认构造函数就没啥用吗?我们在Data类中自定义Time类对象,Time中我们显式定义Time对象的构造函数,而Data中为编译器自动生成。
C++把类型分为内置类型和自定义类型,内置类型即C++中提供的数据类型,如int、char类型等;自定义类型即class、struct等我们自己定义的类型。

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

在这里插入图片描述
我们可以看到,编译器生成的Data的默认构造函数调用了自定义类型成员_t的默认成员函数。故,编译器生成的默认构造函数并非无用,而是具体情况具体实现。

二、析构函数

1 定义
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作由编译器完成。即对象在销毁时自动调用析构函数完成对象中资源的清理工作。
2 特性
(1)函数名为在类名前加上~。
(2)无参数无返回值。
(3)一个类只有一个析构函数,用户未显式定义时,编译器自动默认生成。
析构函数的独一性,则其是不可重载的。
(4)对象生命周期结束时,C++编译系统自动调用析构函数。
我们在Data类中自定义Time类对象_t,Time中我们显式定义Time对象的析构函数,而Data中为编译器自动生成。

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

在这里插入图片描述
程序输出了~Time(),当Data类对象d生命周期结束时,我们并未显式定义Data的析构函数,编译器自动生成默认的析构函数。对象销毁时内置类型不需要资源清理,最后系统直接将其内存回收即可,故只需要销毁自定义类型对象_t,而要将_t对象销毁,就要调用Time类的析构函数,而调用我们显式定义Time类的析构函数。
需清楚,main函数中我们并未直接调用Time类的析构函数,而是显式调用编译器为Data类生成的默认析构函数。

同构造函数,C++语法规定,当用户未显式定义任何析构函数,编译器一定会生成一析构函数;但编译器在具体实现时,可能会考虑到程序的运行效率问题,编译器感觉生成的析构函数并无多大意义时,编译器便不实现此方法。
这又是语法上和程序具体实现上的有所不同。

#include<iostream>
using namespace std;
class Data
{
public:
	Data(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()
{
	Data d1(2022,11,22);
	d1.Print();
	return 0;
}

在这里插入图片描述
我们可以看到,本段程序并未调用Data类的析构函数,此为语法和编译器具体实现上的不同。

(5)当类中未涉及到资源的管理时,用户可不显式定义析构函数,直接使用编译器生成的默认析构函数,如上述的Data类;当类中涉及到资源的管理时,析构函数必须由用户进行显式定义,不然会造成资源的泄露,如Stack类。


#include<iostream>
#include<cstdlib>
using namespace std;
class Stack
{
public:
//初始化栈(构造函数) 
Stack(size_t _capacity = 5)
{
	array = (int*)malloc(sizeof(int)*capacity);
	if (array == nullptr)
	{
		printf("malloc申请空间失败!!!\n");
		return;
	}
	capacity = _capacity;
	size = 0;
	cout << "Stack()" << this<<endl;
}
//析构函数
~Stack()
{
	if (array)
	{
		free(array);
		array =nullptr;
		capacity = 0;
		size = 0;
	}
	cout << "~Stack()"<<this << endl;
}
//栈的相关操作
//入栈时空间不够时申请新的空间函数
void ChackCapacity()
{
	if (size ==capacity)
	{
		//说明空间不够需要开辟新空间
		//1.开辟新空间
		int newcapacity =capacity * 2;
		int* temp = (int*)malloc(sizeof(int)*newcapacity);
		if (NULL == temp)
		{

			printf("申请空间失败!!!\n");
			return;
		}
		//2.空间拷贝
		memcpy(temp, array, sizeof(int)*size);
		//3.把原来空间给释放掉
		free(array);
		//4.使用新空间
		array= temp;
		capacity= newcapacity;
	}
}
//入栈 
void Push(int data)
{
	//如果空间不够得扩容
	ChackCapacity();
	array[size]= data;
	size++;
}
//出栈 
void Pop()
{
	if (Empty())
		return;
	size--;
}
//获取栈顶元素 
int Top()
{
	return array[size-1];
}
//获取栈中有效元素个数 
int Size()
{
	return size;
}
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int Empty()
{
	return 0 == size;
}

private:
	int* array;
	int capacity;
	int size;
};
void Test()
{
	Stack s;
	s.Push(1);
	s.Push(2);
	cout <<s.Size()<< endl;
	s.Pop();
}
int main()
{
	Test();
	return 0;
}

在这里插入图片描述
当对象生命周期结束时,Stack中申请的array的连续空间必须得用户释放,故此时必须实现相应的析构函数以免造成资源的泄露。

接下节:C++类和对象-类的默认成员函数之拷贝构造函数与赋值运算符重载函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值