构造函数
1,构造函数的作用
构造函数是类的一个特殊成员变量,构造函数的作用是在创建对象的时候对对象的数据成员进行初始化
例:
#include<iostream>
using namespace std;
class Box //声明长方体类
{
public:
Box() //定义Box类构造函数,构造函数名与类名相同
{ //利用构造函数对数据成员赋初值
length=1;width=1;height=1;
cout<<"Box's constructor is executed!"<<endl;
}
void show()
{
cout<<"length:"<<length<<endl;
cout<<"width:"<<width<<endl;
cout<<"height:"<<height<<endl;
}
private:
float length,width,height;
};
int main()
{
Box box1; //创建Box类对象box1
cout<<"The information of box1 is:"<<endl;
box1.show();
Box box2; //创建Box类对象box2
cout<<"The information of box2 is:"<<endl;
box2.show();
return 0;
}
程序运行结果如下:
Box's constructor is executed!
The information of box1 is:
length:1
width:1
height:1
Box's constructor is executed!
The information of box2 is:
length:1
width:1
height:1
构造函数不仅具有成员函数的特性,而且还具有它自身的特点:
(1)构造函数与类名相同,且没有返回值,定义构造函数时,在函数名前什么也不能加(void也不可以加);
(2)构造函数不需要用户调用,他是由系统在创建对象时自动调用的。构造函数要声明为public访问属性;
(3)构造函数的作用是在创建对象的时候对对象的数据成员进行初始化,一般在构造函数的函数体里写对类对象的数据成员初始化的语句,但是也可以在其中加上与初始化无关的其他语句。
/*类是一种抽象的自定义数据类型,并不占用内存空间,所以不能在类内对数据成员进行初始化。如下的初始化形式是错误的:*/
class Box
{
...
private:
int length=0; //错误,不能在类内对数据成员进行初始化
...
}
2,带参数的构造函数
带参数的构造函数的一般格式为:
构造函数名(参数表);
参数表与普通函数的参数表是一样的,由参数类型和形参名组成,多个形参之间通过“,”分割;
实参是在定义对象时给出的,一般格式如下:
类名 对象名(实参表)
实参表中的参数类型和个数要与形参表中的对应起来
例:
#include<iostream>
using namespace std;
class Box //声明长方体类
{
public:
Box(float L,float W,float H) //带有三个形参的构造函数
{
length=L;
width=W;
height=L;
}
float Volume()
{
return length*width*height;
}
private:
float length,width,height;
};
int main()
{
Box box(4,2,3);
cout<<"The volume of box is:"<<box.Volume()<<endl;
return 0;
}
程序结果如下:
The volume of box is:24
带参数的构造函数可以很方便的实现创建对象时对不同对象进行不同的初始化
3,构造函数与参数初始化表
在函数首部实现对数据成员的初始化
#include<iostream>
using namespace std;
class Box //声明长方体类
{
public:
//使用参数初始化表的构造函数
Bos(float L,float W,float H):length(L),width(W),height(H){}
float Volume()
{
return length*width*height;
}
private:
float length,width,height;
};
用参数初始化表初始化数据成员的方法方便简练,尤其是在需要初始化的数据成员较多时更显优越性,甚至可以直接在类体中(不是类体外)定义构造函数;
4,构造函数重载
在类中定义多个构造函数,这些构造函数具有相同的函数名(与类名同名),而参数表中参数的个数或类型不同,相当于给类对象实例化的时候提供了不同的初始化方法。这种现象称为构造函数重载
#include<iostream>
using namespace std;
class Box
{
public:
Box() //无参数的构造函数
{
length=1;width=1;height=1;
}
Box(float L,float W,float H) //带有三个形参的构造函数
{
length=L;
width=W;
height=L;
}
float Volume()
{
return length*width*height;
}
private:
float length,width,height;
};
int main()
{
Box box1(4,2,3);
Box box2;
cout<<"The volume of box is:"<<box1.Volume()<<endl;
cout<<"The volume of box is:"<<box1.Volume()<<endl;
return 0;
}
程序运行结果如下:
The volume of box is:24
The volume of box is:1
(1)当没有提供任何构造函数的时候,编译器会给类提供默认的构造函数,默认构造函数什么也不做,是个空函数;
(2)当提供构造函数时,系统不会再生成默认构造函数。
析构函数
析构函数的作用是在系统释放对象占用的内存之前进行一些清理工作,把对象所占用的额外空间归还给程序。
析构函数函数名固定:~类名
没有返回值,不带参数,不能重载。
一个类可以有多个构造函数,但是只有一个析构函数。
#include<iostream>
using namespace std;
class Box
{
public:
Box() //无参数的构造函数
{
length=1;width=1;height=1;
}
Box(float L,float W,float H) //带有三个形参的构造函数
{
length=L;
width=W;
height=L;
}
float Volume()
{
return length*width*height;
}
~Box()
{
//在析构函数中增加输出,当释放对象时会输出所释放对象的相关信息
cout<<"Box("<<length<<","<<width"<<,"<<height<<")";
cout<<"is destructed!"<<endl;
}
private:
float length,width,height;
};
int main()
{
Box box1(4,2,3);
Box box2;
cout<<"The volume of box is:"<<box1.Volume()<<endl;
cout<<"The volume of box is:"<<box1.Volume()<<endl;
return 0;
}
程序输出如下:
The volume of box is:24
The volume of box is:1
Box(1,1,1)is destructed!
Box(4,2,3)is destructed!
析构函数和构造函数的调用次序:
在同一作用域范围内,构造函数和析构函数的调用顺序为:先构造的后析构,后构造的先析构;
何时调用构造函数和析构函数问题小结:
(1)在全局范围中定义的对象(在所有函数之外定义的对象),其构造函数在文件中的所有函数(包含main函数)执行之前调用。如果一个程序中有多个文件,而不同的文件中都定义了全局对象,则这些对象的构造函数执行顺序是不确定的。当main函数执行完毕或调用exit函数时,调用析构函数;
(2)如果定义的是局部自动对象(在函数中定义对象),则在创建对象时调用构造函数,如果函数被多次调用,则在每次创建对象的时候都要调用构造函数。在函数调用结束,释放对象时先调用析构函数;
(3)如果在函数中定义static静态局部对象,则只在程序第一次调用此函数创建对象时调用构造函数一次,在调用结束时对象并不释放,因此也不调用析构函数,只在main函数执行完毕或调用exit函数时,调用析构函数;