C++:构造函数和析构函数

一、构造函数

1.1概念

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

不使用构造函数

cpp 复制代码
#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.Init(2022, 7, 5);//每次都要调用Init函数
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);
	d2.Print();
	return 0;
}

使用构造函数

cpp 复制代码
#include<iostream>
using namespace std;
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(2022, 7, 5);//不需要调用函数直接可以初始化
	d1.Print();
	Date d2(2022,7,6);
	d2.Print();
	return 0;
}

上面两幅图在main函数中,图1未使用构造函数,每次定义时都要调用Init函数初始化。

图2 使用了构造函数,直接定义就可以,编译器会自动调用构造函数初始化,方便了用户,也防止了用户忘记调用Init函数的情况。

1.2特性

1.构造函数是特殊的成员函数,虽然名字叫构造,但作用其实是初始化对象。

2.构造函数的函数名与类的名字相同。

3.在对象实例化时会自动调用对应的构造函数。

4.构造函数没有返回值。

5.构造函数可以重载。

cpp 复制代码
class Date
{
public:
	// 1.无参构造函数
	Date()
	{}
	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestDate()
{
	Date d1; // 调用无参构造函数
	Date d2(2015, 1, 1); // 调用带参的构造函数
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
	// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
	// warning C4930: "Date d3(void)": 未调用原型函数(是否是有意用变量定义的?)
	Date d3();
}

所以Date d3()是错误的用法。

6.如果用户没有定义构造函数,编译器会自动生成一个无参的默认构造函数。如果用户定义了构造函数就不会生成构造函数。

cpp 复制代码
class Date
{
public:

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1; // 自动生成无参的构造函数
	return 0;
}

7.如果编译器自动生成构造函数,对象的初始化由两种情况分为内置类型(基本类型)和自定义类型。对内置类型不做处理,初始化成随机值。对自定义类型会调用其的默认成员函数

cpp 复制代码
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的默认成员函数
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

注:为了解决内置类型初始化成随机值的情况发生,C++11中规定内置类型成员变量在类的声明中可以给默认值。

cpp 复制代码
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 = 1970;//默认值
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

8.无参的构造函数和全缺省的构造函数都称为默认构造函数,且默认构造函数只能存在一个。

cpp 复制代码
class Date
{
public:
	Date()//无参的构造函数
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)//全缺省的函数构造
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 以下测试函数能通过编译吗?
void Test()
{
	Date d1;//编译报错
}

二、析构函数

2.1概念

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

2.2特性

1.析构函数是特殊的成员函数,析构函数是在类名前加上字符~。

2.析构函数无返回值和类型。

3.一个类只能有一个析构函数,若未定义,编译器会默认生成析构函数。

4.析构函数不能重载。

5.对象的生命周期结束时,C++编译系统会自动调用析构函数。

cpp 复制代码
typedef int DataType;
class Stack
{
public:
	Stack(DataType capacity = 3)
	{
		_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()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
int main() 
{
	Stack s;//自动调用Stack构造
	s.Push(1);
	s.Push(2);//自动调用~Stack销毁
	return 0;
}

6.析构函数对于内置类型成员不做资源清理,系统最后会将其内存回收。对于自定义类型会调用

其自身的析构函数。

cpp 复制代码
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类生成的
// 默认析构函数
// 注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的
// 析构函数

7.如果类中没有资源申请(如Date),则可以不写析构函数;如果有资源申请(如Stack),则必须写析构函数。

相关推荐
薄荷故人_4 分钟前
从零开始的C++之旅——红黑树封装map_set
c++
lw向北.5 分钟前
Qt For Android之环境搭建(Qt 5.12.11 Qt下载SDK的处理方案)
android·开发语言·qt
IT女孩儿27 分钟前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript
m0_7482389227 分钟前
webgis入门实战案例——智慧校园
开发语言·ios·swift
悲伤小伞32 分钟前
C++_数据结构_详解二叉搜索树
c语言·数据结构·c++·笔记·算法
Clockwiseee41 分钟前
PHP伪协议总结
android·开发语言·php
小灰灰搞电子42 分钟前
Qt实现Android的图案密码(图形解锁)源码分享
开发语言·qt
m0_675988232 小时前
Leetcode3218. 切蛋糕的最小总开销 I
c++·算法·leetcode·职场和发展
吴冰_hogan2 小时前
JVM(Java虚拟机)的组成部分详解
java·开发语言·jvm
白宇横流学长3 小时前
基于java出租车计价器设计与实现【源码+文档+部署讲解】
java·开发语言