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

本文详细阐述了C++中的构造函数和析构函数,包括它们的概念、特性、调用时机以及在内存管理中的作用。构造函数用于对象初始化,析构函数负责对象销毁时的清理工作。内置类型和自定义类型在析构时的行为差异也做了说明。
摘要由CSDN通过智能技术生成

一、构造函数

1.1概念

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

不使用构造函数

#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;
}

 使用构造函数

#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.构造函数可以重载。

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.如果用户没有定义构造函数,编译器会自动生成一个无参的默认构造函数。如果用户定义了构造函数就不会生成构造函数。

class Date
{
public:

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

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

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中规定内置类型成员变量在类的声明中可以给默认值。


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.无参的构造函数和全缺省的构造函数都称为默认构造函数,且默认构造函数只能存在一个。

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++编译系统会自动调用析构函数。

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

其自身的析构函数。

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),则必须写析构函数。

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值