c++程序中的初始化和清理工作,分别由两个特殊成员函数来完成,他们就是构造函数和析构函数。
构造函数
c++是一种面向对象的编程语言,它要让程序员使用类对象就像使用标准类型一样。
比如要你写一个函数,它的功能就好像int、char一样,能初始化一个Clock类对象,并且能将值赋给它的数据成员。而构造函数就可以定义这么一个对象,还可以同时对它的数据成员赋初值。
构造函数的作用就是在对象被创建时利用特定值构造一个对象,将对象初始化为一个特定状态。此外,它还有两个特殊性质:
- 构造函数的函数名和类名相同,且没有返回值
- 构造函数通常被声明为公有函数
#include<iostream>
using namespace std;
class Clock{
public:
Clock(int newH, int newM, int newS); //构造函数
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
void Clock::setTime(int newH, int newM, int newS){
hour=newH;
minute=newM;
second=newS;
}
void Clock::showTime(){
cout<<hour<<":"<<minute<<":"<<second<<endl;
}
Clock::Clock(int newH, int newM, int newS){
hour=newH;
minute=newM;
second=newS;
}
int main(){
Clock c(0, 0, 0); //调用构造函数建立对象c
c.showTime();
c.setTime(8, 30, 30);
c.showTime();
return 0;
}
运行结果如下:
代码中的Clock c(0, 0, 0)
就等于如下:、
Clock c = Clock(0, 0, 0);
这是显式调用,但是我们更经常用Clock c(0, 0, 0)
这样格式紧凑的隐式调用。
如果main()里面这样声明对象:
Clock c;
编译器就会报错,因为面向对象的时候,你得给出必要的实参来。
还有一点需要注意的是,类成员名称(hour,minute,second)
不能和构造函数参数名称(newH,newM,newS)
设为一样,因为我们要将newH
赋值给hour
,等等。
至于默认构造函数和拷贝构造函数等概念,我们会在下次讲到。
析构函数
我们做事情的时候都要做好扫尾工作,写程序的时候当然也要。用构造函数创造的对象,程序会一直跟踪,直到其过期,由析构函数来完成清理工作。比如说一个函数中定义了几个局部对象,那么当这函数运行结束返回调用者时,函数中的对象也随之消失,消失的时候需要函数来释放其所占用的内存单元,这个函数就是析构函数。
简单来说,析构函数与构造函数作用相反,用来完成对象被删除前的一些清理工作。而且析构函数在对象生存期即将结束时被自动调用,调用之后,对象也就消失了,内存也被释放。
析构函数在类名称前加~
,和构造函数一样的是,析构函数没有返回值和声明类型,与构造不同的是,析构函数没有参数。接着我们依旧拿原来的代码开刀:
#include<iostream>
using namespace std;
class Clock{
public:
Clock(int newH, int newM, int newS); //构造函数
void setTime(int newH, int newM, int newS);
void showTime();
~Clock() {cout<<"调用析构函数"<<endl;} //析构函数
private:
int hour, minute, second;
};
void Clock::setTime(int newH, int newM, int newS){
hour=newH;
minute=newM;
second=newS;
}
void Clock::showTime(){
cout<<hour<<":"<<minute<<":"<<second<<endl;
}
Clock::Clock(int newH, int newM, int newS){
hour=newH;
minute=newM;
second=newS;
}
int main(){
Clock c(0, 0, 0);
c.showTime();
c.setTime(8, 30, 30);
c.showTime();
return 0;
}
运行结果如下:
通过程序,我们可以看到析构函数等到对象被使用结束才被调用到。
因为一般情况下析构函数不担当重要作用,我们可以将其编写为不执行任何操作的函数:
~Clock() {}
但是为了能方便看出析构函数何时被调用,我们这样写:
~Clock() {cout<<"调用析构函数"<<endl;}
一般的小函数,我们是不用自己来写析构函数的,因为编译器会隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,提供默认析构函数定义。
但是有一种情况是,如果你对象通过new
来创建,它将驻留在栈内存或自由存储区中,这时就必须使用delete
来释放内存,其析构函数也将自动被调用。