类和对象中的构造函数和析构函数

首先我们先来大致了解类中的6个默认成员函数

当一个类中任何成员都没有的时候,我们就简称其为空类,但是我们要知道的是,空类当中也并不是什么都没有,任何一个类,在我们不去写的情况下,都会自动生成下面的6个默认成员函数。这就是类中的6个默认成员函数。

这6个默认成员函数可以分为以下三类:

  1. 初始化和清理
    构造函数:完成初始化工作
    析构函数:完成清理工作
  2. 拷贝复制
    拷贝构造:使用同类对象初始化创建对象
    赋值重载:把一个对象赋值给另一个对象
  3. 取地址重载
    普通对象取地址
    const对象取地址

接下来我们先来看其中的构造函数和析构函数

构造函数
先来看一个日期类:

#include <iostream>

using namespace std;

class Date
{
	int m_year;
	int m_month;
	int m_day;
public:
	void SetDate(int year, int month, int day)
	{
		m_year = year;
		m_month = month;
		m_day = day;
	}

	void Display()
	{
		cout << m_year << '-' << m_month << '-' << m_day << endl;
	}
};

int main()
{
	Date d1, d2;
	d1.SetDate(2019, 12, 1);
	d1.Display();

	d2.SetDate(2019, 10, 1);
	d2.Display();

	system("pause");
	return 0;
}

在这个Date类当中,我们可以通过SetDate这个公有的成员函数来给对象设置内容,但是如果每创建一次对象,都调用这个函数去设置信息,会显得有些麻烦,所以就考虑能不能在创建对象的时候,就将信息设置进去!

这就要用到构造函数了,构造函数其实是一个特殊的成员函数,它的名字与类名相同,在创建类类型的对象时由编译器自动调用,这样保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只会调用一次!

需要注意的是构造函数的名字叫做构造,但它的任务并不是开辟空间创建对象,而是初始化对象。

构造函数的特征

  1. 函数名与类名相同
  2. 没有返回值
  3. 对象实例化时编译器会自动调用对应的构造函数
  4. 构造函数可以重载

比如把我们上面的日期类改为下面这样:

#include <iostream>

using namespace std;

class Date
{
	int m_year;
	int m_month;
	int m_day;
public:
	//无参构造函数
	Date()
	{

	}

	//带参构造函数
	Date(int year, int month, int day)
	{
		m_year = year;
		m_month = month;
		m_day = day;
	}

	void Display()
	{
		cout << m_year << '-' << m_month << '-' << m_day << endl;
	}
};

int main()
{
	Date d1; //调用无参构造
	Date d2(2019, 1, 1); //调用带参的构造函数

	//注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,不然就变成了函数声明
	//比如下面这样,声明了d3函数,这个函数没有参数,返回值是个Date类型的对象。
	Date d3();

	system("pause");
	return 0;
}

如果我们在类中没有显示定义构造函数那么我们的C++编译器会自动生成一个无参的默认构造函数,而当我们自己显式定义之后,编译器将不再生成。比如下面这样:

class Date
{
	int m_year;
	int m_month;
	int m_day;
};

void Test()
{
	Date d;
}

像这样没有定义构造函数,对象也是可以创建成功的,因为这里调用的是编译器生成的默认构造函数

无参的构造函数和全部缺省的构造函数都称为默认构造函数,并且一定要注意默认构造函数只能有一个
无参构造函数,全缺省构造函数,我们不去定义而编译器自己生成的构造函数,都可以认为是默认构造函数。
可以试试下面这段代码能否通过编译

#include <iostream>

using namespace std;

class Date
{
	int m_year;
	int m_month;
	int m_day;
public:

	Date()
	{
		m_year = 2019;
		m_month = 12;
		m_year = 1;
	}

	//带参构造函数
	Date(int year = 2008, int month = 8, int day = 8)
	{
		m_year = year;
		m_month = month;
		m_day = day;
	}

};

int main()
{
	Date d1;

	system("pause");
	return 0;
}

析构函数
通过构造函数的学习,我们知道一个对象是怎么来的,那么一个对象又是怎么没的呢?
析构函数与构造函数功能相反,析构函数并不是完成对对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁的时候会自动调用析构函数,完成类的一些资源清理工作。

析构函数也是特殊的成员函数

析构函数的特征

  1. 析构函数名是在类名前加上字符~
  2. 没有参数返回值
  3. 每一个类都只有一个析构函数。如果我们没有显式定义析构函数,系统会生成默认的析构函数
  4. 对象的生命周期结束的时候,C++编译系统自动调用析构(一般在return 0 之后)

看下面这段代码,了解一下析构函数的使用:

typedef int DataType;

class SeqList
{
	int * m_data;
	size_t m_size;
	size_t m_capacity;
public:
	SeqList(int capacity = 10)
	{
		m_data = (DataType *)malloc(capacity * sizeof(DataType));
		assert(m_data);

		m_size = 0;
		m_capacity = capacity;
	}

	~SeqList()
	{
		if (m_data)
		{
			free(m_data); //释放堆上空间
			m_data = NULL; //将指针置为NULL,防止变为野指针
			m_capacity = 0;
			m_size = 0;
		}
	}

};		

构造函数总结

  1. 它是一个特殊的成员函数,它不存在返回值,名字和类名相同,在实例化对象的时候自动调用.
  2. 系统会自动提供一个默认的构造函数,如果自己实现了构造函数(1),则系统不再提供默认的构造函数.
  3. 构造函数可以存在参数,它与其他的构造函数是以函数重载的方式共同存在的.
  4. 拷贝构造函数指的是参数为本类其他对象的引用的构造函数,它在给对象初始化成本类其他对象时调用,系统会自动提供一个拷贝构造函数

析构函数总结

  1. 析构函数是当一个栈被销毁前调用的,在C++中,当一个函数栈被销毁前,会调用栈中每一个对象的析构函数.
  2. 析构函数不存在参数也不存在返回值.它的名字是类名前加波浪线().外部写要写为类名::+类名.
  3. 系统会自动提供一个什么都不做的析构函数

关于浅拷贝和深拷贝
浅拷贝: 直接复制内存
深拷贝: 当内存成员中有指向堆的指针,就必须重新给该指针分配空间,然后将目标对象指针所指空间的内容拷贝到新分配的空间.(如果不这样做,会导致两个指针指向同一片空间,从而在析构中多次释放).

发布了235 篇原创文章 · 获赞 28 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44781107/article/details/103337565