大家都知道,C++的类构成中会有构造函数和析构函数。
而构造函数又分为默认构造函数、默认拷贝构造函数和自定义的构造函数和自定义的拷贝构造函数。
构造函数
1、构造函数必须与类同名。
2、C++允许构造函数重载。
3、构造函数没有返回值。
当对象被创建时,自动调用构造函数。
C++对于类都会自动生成“默认构造函数”和“默认拷贝构造函数”。
构造函数调用顺序
case1:当普通构造一个对象时(例如:Test a;),程序先自动调用默认构造函数分配空间,再调用自定义构造函数(如果有的话)。
case2:当拷贝构造一个对象时(例如:Test b(a);),程序先自动调用默认构造函数分配空间,然后分两种情况,如果程序有自定义拷贝构造函数,则调用自定义拷贝构造函数;如果没有自定义拷贝构造函数,那么就调用默认拷贝构造函数进行浅拷贝。
注意:case2的情况下,当程序自定义拷贝构造函数了,那么程序是不会调用默认构造函数进行浅拷贝,我们需要在自定义的拷贝构造函数里把所有需要拷贝的成员变量都写上,无论是要深拷贝还是浅拷贝。
默认构造函数
默认构造函数是 不带参数的。
默认构造函数的作用是,给类中的变量分配内存空间。
对于类,编译器都会默认生成一个“不带参数的构造函数”,形式如下:
class Test
{
public:
Test() // 默认构造函数
{
//这里面做一些变量内存分配的事..
}
}
默认拷贝构造函数
默认拷贝构造函数是 只有一个参数并且参数是该类对象的引用的。
对于类,编译器会默认生成一个“只有一个参数并且参数是该类对象的引用的 构造函数”,形式如下:
class Test
{
public:
Test(const Test& obj) //默认拷贝构造函数
{
//这里做一些变量内存分配,已经浅拷贝的事..
}
}
默认拷贝构造函数仅仅做了类成员内存空间分配和简单的值拷贝,即:浅拷贝。
关于深拷贝、浅拷贝,请参考点击我。
自定义构造函数
除了系统自动生成的默认构造函数和默认拷贝构造函数,我们自己代码里些的构造函数就是自定义构造函数。随便举个例子,如下:
class Test
{
public:
Test() //自定义构造函数,也可以不带参数
{}
Test(int a) // 自定义构造函数,带一个int参数
{}
Test(long int b)
{}
Test(const Test& obj) //自定义拷贝构造函数,带一个参数并且参数是该类对象的引用
{
//可以做一些自定义的深拷贝动作..
}
...
}
析构函数
当对象被销毁时,自动调用析构函数。
析构函数也分为默认析构函数和自定义析构函数。
析构函数调用顺序
析构时,编译器先调用自定义析构函数(如果有的话),释放一些引用的堆内存,系统资源等等;然后,再调用默认析构函数,对类的一些普通数据所占用的内存进行释放。
那么,对象什么时候会被销毁?
1、全局对象在程序结束时,会被销毁。
2、局部对象,在局部函数退出的时候,就会被销毁。
默认析构函数
任何类,编译器都会生成一个默认析构函数,形式如下:
class Test
{
public:
~Test() //默认析构函数
{
// 做 "类的普通变量的内存销毁" 这事..
}
}
默认析构函数都干了什么事?
默认析构函数只会释放类的普通数据所占用的空间, 但是像new或者malloc申请的堆的内存,它是不会去释放的,所以这部分需要我们自己做。
因此,就有了自定义析构函数。往下看。
自定义析构函数
形式如下:
class Test
{
public:
~Test() //自定义析构函数
{
// 如果有new或者malloc,对系统资源调用等,可以在这里销毁
}
}
如上所述,在自定义析构函数中,主要做:
1、一些用户自己申请的堆内存(new / malloc)的释放。
2、系统资源的释放(FILE*等等)。
一些细节知识点
拷贝构造函数是什么时候会被调用?
1、当用对象去赋值时,会调用拷贝构造函数。
例如:
class Test
{};
Test a;
Test b(a); //这时候会调用拷贝构造函数
Test c = a; //和上面同义,这时候也会调用拷贝构造函数
2、当函数调用时,用实参初始化形参的时候,会调用拷贝构造函数。
例如:
class Test
{};
void TestFunc_1(Test obj) //对象作为函数参数
{}
void TestFunc_2(Test& obj) //对象的引用作为函数参数
{}
void main()
{
Test a;
Test &b = a;
TestFunc_1(a); //对象直接作为参数,会调用拷贝构造函数
TestFunc_2(b); //对象的引用作为参数,不会调用拷贝构造函数
}
3、当函数return一个对象时,也会调用拷贝构造函数。
例如:
class Test
{};
Test global;
Test TestFunc_1()
{
return global;
}
Test& TestFunc_2()
{
return global;
}
void main()
{
Test a = TestFunc_1(); //return的是类,会调用拷贝构造函数
Test b = TestFunc_2(); //return的是类的引用,不会调用拷贝构造函数
}