【C++】之类和对象 - 构造与析构

目录

一.构造函数(无参&&有参)

1.概念

2.特性

3.代码

4.默认构造函数

二.析构函数

1.概念

2.特性:

3.有自定义类型的析构调用顺序

三.拷贝构造函数

1.概念

2.特性

3.拷贝构造的参数问题

4.编译器自动生成的拷贝构造

5.哪些场景会调用拷贝构造


一.构造函数(无参&&有参)

1.概念

构造函数是一个特殊的成员函数,名字与类名相同,创建类对象时由编译器自动调用,并且在对象的整个生命周期内只调用一次

2.特性

1).构造函数,并不开辟空间创建对象,而是对已经创建的对象初始化

2).函数名与类名相同

3).无返回值

4).对象实例化时编译器自动调用

5).构造函数支持重载

6).对内置类型(char,int,double...)不做处理,对自定义类型,自动调用自定义类型的构造函数

3.代码

class Date
{
public:

	//1.无参构造函数(如果不写构造函数,则系统默认生成)
	Date()
	{

	}
	//2.有参构造函数(与无参构造函数发生重载)
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//3.带缺省参数的有参构造
	//Date(int year, int month = 3, int day = 10)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;//调用无参构造 注意:不要写成Date d1()这样会被当作函数声明
	Date d2(2022, 8, 7);//调用有参构造

	return 0;
}

4.默认构造函数

一般把默认构造函数分为三类:

        编译器自动生成的无参构造函数

        全缺省的有参构造函数

        我们自己写的无参构造函数

默认构造函数只能有一个,本质原因就是如果有多个的话,无法发生重载

代码:

所以当我们没有写构造函数的时候,编译器自动生成的构造函数有什么用呢?

作用就在于,编译器默认生成的是构造函数,只要是构造函数,如果有自定义类型,就会对自定义类型调用其自己的默认构造函数。

构造函数对内置类型不做处理,我们可以看到调用编译器生成的构造时,仍然还是随机值

C++11中对于构造函数内置类型不初始化这一缺陷打了一个补丁:

内置类型成员在声明时,可以以缺省值的方式给默认值

class Date
{
public:

private:
    //这里用给缺省值的方式,来给内置类型设置默认值,注意这里仍然是声明
	int _year = 2022;
	int _month = 8;
	int _day = 7;
};
int main()
{
	Date d1;
	return 0;
}

二.析构函数

1.概念

与构造函数功能相反,构造函数完成了对刚创建的对象的初始化,析构函数完成了对即将销毁的对象的数据释放

析构函数不是完成对对象本身的销毁,对象销毁与对象创建一样,由编译器完成,对象在销毁时自动调用析构函数,完成对象中的资源清理

2.特性:

1).析构函数名是在类名之前加上~

2).无参无返回值

3).一个类只能有一个析构函数

4).若没有显式定义则系统自动生成默认析构函数

5).对象生命周期结束时,编译器自动调用析构

6).如果有自定义类型,该类析构函数会自动调用自定义类型中的析构函数

3.有自定义类型的析构调用顺序

class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
		cout << "调用Time构造函数" << endl;
	}
	~Time()
	{
		cout << "调用Time析构函数" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date()
	{
		_year = 2022;
		_month = 8;
		_day = 7;
		cout << "调用Date构造函数" << endl;
	}
	~Date()
	{
		cout << "调用Date析构函数" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d1;
	return 0;
}

构造顺序:首先编译器创建出对象d1,在给d1初始化时,需要先调用对象t的构造,t初始化好之后,再调用Date构造 

析构顺序:编译器不会直接去调用Time析构,而是先调用Date析构,Date析构在清理数据时发现,还需要清理对象t,然后接着调用Time析构

如果没有在堆区创建的对象,一般直接使用默认生成的就ok了,如果有需要释放的数据空间则一定要显示去写析构函数

此外,还要记住一点,后构造的先析构

三.拷贝构造函数

1.概念

拷贝构造是构造函数的一种,在对象创建时,将一个对象完整的拷贝给另一个对象

拷贝构造函数是对一个对象的初始化,而不是赋值!

2.特性

1).拷贝构造函数是构造函数的一个重载形式

2).参数只有一个且必须是类类型对象的引用,如果以传值的方式将直接报错

3).如果没有显示定义,编译器会自动生成默认的拷贝构造函数(浅拷贝)

3.拷贝构造的参数问题

参数必须是类类型对象的引用

	//因为仅仅只是拷贝,而又必须使用传引用的方式
    //防止因误操作将原先数据修改,一般在前面加上const
    Date(const Date& d)
	{
		*this = d;
	}

如果直接传值可不可以呢,例如Date(const Date d),这样不行,会发生无限递归调用拷贝构造

~~如果是传值

因为要调用拷贝构造,但每次传值的本质就是拷贝(编译器新创建一个变量然后需要调用拷贝构造来初始化这个变量)就又需要调用拷贝构造,这样一直循环下去

~~如果是传引用

传引用的本质就是传地址,传地址不需要调用拷贝构造

4.编译器自动生成的拷贝构造

编译器自动生成的拷贝构造是一种浅拷贝

对于内置类型按照字节直接拷贝(浅拷贝)对于自定义类型调用其自己的拷贝构造

当没有涉及到资源申请的时候,拷贝构造是否显式去写都可以,一旦涉及到malloc或者new,那么拷贝构造是一定要写的,如果不写,就用编译器生成的那一定是浅拷贝,本来1个指针指向一块空间,拷贝之后并没有拷贝空间而是直接拷贝了一个指针,2个指针指向同一个空间,很容易出问题

5.哪些场景会调用拷贝构造

1).用类实例化对象,且用已有对象初始化

2).函数传参类型为类的类型

3).函数返回值为类的类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>