构造函数与析构函数

介绍

构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,不需要手动调用。

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

使用

构造函数分为三种:默认构造函数,有参构造函数,拷贝构造函数

其使用规则如下:

class Person
{
	public:
		Person()
		{
			cout<<"默认构造函数调用"<<endl; 
		}
		Person(int age)
		{
			my_age = age;
			cout<<"有参构造函数的调用"<<endl;
		}
		Person(const Person &p)
		{
			cout<<"拷贝构造函数的调用"<<endl;
			my_age = p.my_age;
		}
		~Person()
		{
			cout<<"析构函数的调用"<<endl;
		}
		int my_age;
 };

直接以类名作为函数名,不用写void

使用规则

默认情况下,C++编译器至少给一个类添加3个函数 
1.默认构造函数 
2.默认析构函数 
3.默认拷贝构造函数 

构造函数的调用规则
1.创建一个类,C++编译器至少给一个类添加3个函数
默认构造
析构函数
拷贝构造

注意:当我们定义了有参构造函数后编译器就不会提供默认构造函数,但会提供默认拷贝构造函数;当我们定义了拷贝构造函数,那么编译器就不会提供其他的构造函数。

关于拷贝构造函数

关于拷贝构造函数有以下三点

使用一个已经创建完毕的对象来初始化一个新的对象

这是拷贝构造函数最基本的用法

void test01()
{
	Person p1(20);
	cout<<p1.my_age<<endl;
	Person p2(p1);
	cout <<p2.my_age<<endl;
}

在代码中,p2就是由p1直接初始化而来。

注意:此时拷贝出来的p2与p1的地址完全相同,也称为浅拷贝。

值传递的方式给函数参数传值

void doWork(Person p)
{
	Person p2(p);
	cout<<"p2:"<<p2.my_age<<endl;
}
void test02()
{
	Person p(100);
	doWork(p); 
	
}

运行结果如上,现在我们来分析一下

按理来说,应该只会调用一次拷贝构造函数,但运行结果告诉我们它调用了三次。

其实在我们将p作为形参传给doWork时编译器为我们调用了拷贝构造函数。

所以,实参传给形参的时候,它会调用一个拷贝构造函数,它拷贝出来的是一个临时的副本

值方式返回局部对象 

Person doWork2()
{
	Person p1;
	cout<<(int*)p1.my_age<<endl;
	return p1;
}

void test03()
{
	Person p = doWork2();
	cout<<(int *)p.my_age<<endl;
 } 

当对象作为返回值时,我们同样来看一下运行结果

我们会看到p1与p的地址一样,所以这代表了doWork函数的返回值是 p1吗,其实不然,

我们看到的结果其实是经过了编译器的优化,其实并不是返回p1,而是创建一个副本然后拷贝p1,再返回 

浅拷贝与深拷贝

浅拷贝:简单的赋值拷贝操作 

深拷贝:在堆区重新申请空间,进行拷贝操作 

浅拷贝就是上文中所应用的,就不多做赘述了

class Person{
	public:
		Person()
		{
			cout<<"默认构造函数"<<endl;
		}
		Person(int age,int height)
		{
			my_age = age;
			my_height = new int (height);
			cout<<"有参构造函数的调用"<<endl;
		}
		Person(const Person &p)
		{
			my_height = new int(*p.my_height);
			cout<< "拷贝构造函数的调用"<<endl;
		}
		~Person()
		{
			if(my_height!=NULL)
			{
				delete my_height;
				my_height = NULL;
			}
			cout<<"析构函数的调用"<<endl;
		 } 
		int my_age;
		int *my_height; 
};

void test01()
{
	Person p1(18,160);
	cout<<"p1的年龄为:"<<(int *)p1.my_age<<endl;
	Person p2(p1);
	cout<<"p2的年龄为:"<<(int *)p2.my_age<<endl;
}
		Person(int age,int height)
		{
			my_age = age;
			my_height = new int (height);
			cout<<"有参构造函数的调用"<<endl;
		}

在这段代码中,我们为了防止指针悬挂,我们new了一个堆区空间,所以在析构函数中我们要将其delete掉,这时候就有一个问题了,如果我们是浅拷贝,那么p1,p2中的my_age是同一地址,当编译器调用析构函数时,会先析构掉p2,那么这块堆区就是NULL了,就会导致p1析构时报错。为了避免这一情况,我们就要使用深拷贝了。

	Person(const Person &p)
		{
            my_age = p.my_age;
			my_height = new int(*p.my_height);
			cout<< "拷贝构造函数的调用"<<endl;
		}

也就是在拷贝构造函数中new一个堆区空间,对其进行存储。这样就不会出现浅拷贝那样的问题了。

通过学习B站黑马程序员的笔记。

ok,如有不足或错误欢迎指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值