构造函数
C++中通常用构造函数来对对象中的数据成员进行初始化,构造函数与类名同名。构造函数没有返回值,只对对象进行初始化。在建立对象时自动调用构造函数,每建立一个对象就调用一次构造函数。
析构函数
析构函数的作用与构造函数相反。名字也与类名相同,只是在类名前加一个“~”符号。当对象的生命周期结束时,释放对象前自动调用析构函数。
静态局部变量在main函数结束时或者调用exit()函数结束程序时,才调用static局部对象的析构函数。
全局对象,在程序的流程离开作用域时,调用全局对象的析构函数。
用new运算符创建的对象,在用delete运算符释放该对象时,先调用该对象的析构函数。
析构函数的作用不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作。不能被重载,一个类中只能有一个析构函数。
构造函数与析构函数的调用次序
析构函数的调用顺序与构造函数相反,最先被调用的构造函数最后调用它的析构函数,最后被调用的构造函数先调用它的析构函数。
1)全局对象的构造函数在本文件模块中的所有函数执行之前被调用。若一个程序包含多个文件,而在这些不同的文件中都定义了全局对象,则这些对象的构造函数的执行顺序不确定。当main函数执行完或者调用exit函数时,调用析构函数。
2)局部对象在建立对象时调用构造函数。若对象所在的函数被调用多次,则在每次建立对象时都会调用构造函数。函数调用结束、对象释放前先调用析构函数。
3)静态局部对象只在程序第一次调用此函数,定义对象时调用构造函数,函数调用结束对象不释放,不调用析构函数。在main函数结束或调用exit函数时调用析构函数。
派生类的构造函数与析构函数
基类的构造函数只对基类的数据成员进行初始化,派生类的构造函数只对派生类新增的数据成员进行初始化,同时也会调用基类的构造函数。
用派生类建立一个对象时,执行构造函数的顺序是:先调用基类的构造函数,再执行派生类构造函数本身。
释放对象时,执行析构函数的顺序是:先调用子类的析构函数,再调用基类的析构函数
定义基类:
class A
{
public:
A()
{
cout << "AA" << endl;
}
~A()
{
cout << "~AA" << endl;
}
};
定义子类:
class B :public A
{
public:
B()
{
cout << "BB" << endl;
}
~B()
{
cout << "~BB" << endl;
}
};
建立基类对象,调用基类的构造函数:
A* pA = new A();//输出AA
释放基类对象,调用基类的析构函数:
delete pA; //输出~AA
建立子类对象,先调用基类的构造函数,再执行子类的构造函数
B* pB = new B();//先调用基类的构造函数,输出AA,再调用子类的构造函数,输出BB
释放子类对象,先调用子类的析构函数,再调用基类的析构函数
delete pB; //先调用子类的析构函数,输出~BB,再调用基类的析构函数,输出~AA
main函数:
int main()
{
A* pA = new A();//输出AA
delete pA; //输出~AA
B* pB = new B();//
delete pB;
return 0;
}
执行结果:
基类指针指向子类对象,先调用基类的构造函数,再执行子类构造函数
pA = new B();//先调用基类的构造函数输出AA,再调用子类的构造函数,输出BB
释放指针,这里只调用了基类的析构函数,子类的析构函数并没有调用。按理说是要调用子类的析构函数的,那么这就有问题了。该怎么解决呢?就是在基类中定义虚析构函数
delete pA; //输出~AA
在基类中定义虚析构函数
class A
{
public:
A()
{
cout << "AA" << endl;
}
virtual ~A()
{
cout << "~AA" << endl;
}
};
子类
class B :public A
{
public:
B()
{
cout << "BB" << endl;
}
~B()
{
cout << "~BB" << endl;
}
};
将基类指针指向子类对象,释放基类指针时,就会先调用子类的析构函数,再调用基类的析构函数。
int main()
{
A* pA = new A();//输出AA
pA = new B();//基类指针指向子类对象,先调用基类的构造函数输出AA,再调用子类的构造函数,输出BB
delete pA;
return 0;
}
此时的执行结果为: