介绍
构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,不需要手动调用。
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
使用
构造函数分为三种:默认构造函数,有参构造函数,拷贝构造函数
其使用规则如下:
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,如有不足或错误欢迎指正。