何时调用构造函数和析构函数
构造函数的作用是保证每个对象的数据成员都有何时的初始值。
析构函数的作用是回收内存和资源,通常用于释放在构造函数或对象生命期内获取的资源。
一般我们都知道构造和析构的次序:
构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。
构造函数和析构函数都是由编译器隐式调用的。这些函数的调用顺序取决于程序的执行进入和离开实例化对象时所在的那个作用域的顺序。一般而言,析构函数的调用顺序和构造函数的调用顺序相反,但是,对象的存储类可以改变析构函数的调用顺序。
对于在全局作用域中定义的对象,它们的构造函数是在文件中所有其他函数(包括main)开始执行之前被调用的(但无法保证不同文件的全局对象构造函数的执行顺序)。对应的析构函数是在终止main之后调用的。
exit函数会迫使程序立即终止,而不会执行自动对象的析构函数。这个函数经常用来在检测到输入错误或者程序所处理的文件无法打开时终止程序。
abort函数与exit函数功能相似,但它会迫使程序立即终止,而不允许调用任何对象的析构函数。abort函数通常用来表明程序的非正常终止。
自动局部变量的构造函数是在程序的执行到达定义这个对象的位置时调用的,而对应的析构函数是在程序离开这个对象的作用域时调用的(即定义这个对象的代码完成了执行)。每次执行进入和离开自动对象的作用域时,都会调用它的构造函数和析构函数。如果程序调用了exit或abort函数而终止,则不会调用自动对象的析构函数。
静态局部对象的析构函数只调用一次,即执行首次到达定义这个对象的位置时。对应的析构函数是在main终止或程序调用exit函数时调用的。
全局对象和静态对象是以创建它们时相反的顺序销毁的。如果程序由于调用了exit函数而终止,则不会调用静态对象的析构函数。
下面的demo演示了在几个作用域不同的存储类的CreateAndDestory的类的对象,它们的构造函数和析构函数的调用顺序。
-
#include <iostream>
-
#include <string>
-
using
namespace
std;
-
-
class Demo
-
{
-
public:
-
Demo(
int,
string);
//构造函数
-
~Demo();
//析构函数
-
private:
-
int objectID;
//ID number for object
-
string message;
//message describing object
-
};
-
-
Demo::Demo(
int ID,
string messagestring)
-
{
-
objectID = ID;
//set object’s ID number
-
message = messagestring;
//set object’s descriptive message
-
-
cout<<
“Object “<<objectID<<
” constructor runs “<<message<<
endl;
-
}
-
-
Demo::~Demo()
-
{
-
cout<<(objectID==
1 || objectID==
6 ?
“\n” :
“”);
-
-
cout<<
“Object “<<objectID <<
” destructor runs “<<message<<
endl;
-
}
-
-
void create(void);
-
-
Demo first(1,“(main 前的全局对象)”);
//在全局作用域下定义first对象。它的构造函数是在执行main中的任何语句之前调用的
-
//而它的析构函数是在已经运行了所有其他对象的析构函数之后,程序终止时调用
-
-
int _tmain(
int argc, _TCHAR* argv[])
-
{
-
cout<<
“\n main 函数开始执行:”<<
endl;
-
Demo second(2,“(main 函数中的局部自动对象)”);
-
static Demo third(3,“(main 函数中的静态局部对象)”);
-
-
create();
-
-
cout<<
“\n main 函数:继续执行”<<
endl;
-
Demo fourth(4,“(main 函数中的局部自动对象)”);
-
cout<<
“\n main 函数:执行结束”<<
endl;
-
system(
“pause”);
-
return
0;
-
}
-
-
void create(void)
-
{
-
cout<<
“\n create 函数:开始执行”<<
endl;
-
Demo fifth(5,“(cerate 函数里的本地自动对象)”);
-
static Demo sixth(6,“(cerate 函数里的本地静态对象)”);
-
Demo seventh(7,“(cerate 函数里的本地自动对象)”);
-
cout<<
“\ncerate 函数:执行结束”<<
endl;
-
}
运行结果:
Object 6 destructor runs (cerate 函数里的本地静态对象)
Object 3 destructor runs (main函数中的本地静态对象)
Object 1 destructor runs (main函数前的全局对象)
分析:
Main函数中声明了3个对象,second对象,fourth对象,是局部自动对象,而third对象是一个静态局部对象。当执行到达对象的声明位置时,才会调用这些对象的构造函数。
当执行到达mian的末尾时,首先调用fourth对象的析构函数,然后是second对象的析构函数。由于third对象是静态的,因此它会存活到程序终止。调用third对象的析构函数的时机,是在调用全局对象first的析构函数之前,但在所有其他对象被销毁之后。
Create函数声明了3个对象,其中fifth和seventh是局部自动对象,sixth是静态局部对象。当create终止时,首先调用seventh对象的析构函数,然后是fifth对象的析构函数。由于sixth对象是静态的,因此它会存活到程序终止。调用sixth对象的析构函数的时机,是在调用third和first的析构函数之前,但在所有其他对象被销毁之后。
参考资料:《c++程序员教程》 电子工业出版社 张良华 译 P259-262
</div>
何时调用构造函数和析构函数
构造函数的作用是保证每个对象的数据成员都有何时的初始值。
析构函数的作用是回收内存和资源,通常用于释放在构造函数或对象生命期内获取的资源。
一般我们都知道构造和析构的次序:
构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。
构造函数和析构函数都是由编译器隐式调用的。这些函数的调用顺序取决于程序的执行进入和离开实例化对象时所在的那个作用域的顺序。一般而言,析构函数的调用顺序和构造函数的调用顺序相反,但是,对象的存储类可以改变析构函数的调用顺序。
对于在全局作用域中定义的对象,它们的构造函数是在文件中所有其他函数(包括main)开始执行之前被调用的(但无法保证不同文件的全局对象构造函数的执行顺序)。对应的析构函数是在终止main之后调用的。
exit函数会迫使程序立即终止,而不会执行自动对象的析构函数。这个函数经常用来在检测到输入错误或者程序所处理的文件无法打开时终止程序。
abort函数与exit函数功能相似,但它会迫使程序立即终止,而不允许调用任何对象的析构函数。abort函数通常用来表明程序的非正常终止。
自动局部变量的构造函数是在程序的执行到达定义这个对象的位置时调用的,而对应的析构函数是在程序离开这个对象的作用域时调用的(即定义这个对象的代码完成了执行)。每次执行进入和离开自动对象的作用域时,都会调用它的构造函数和析构函数。如果程序调用了exit或abort函数而终止,则不会调用自动对象的析构函数。
静态局部对象的析构函数只调用一次,即执行首次到达定义这个对象的位置时。对应的析构函数是在main终止或程序调用exit函数时调用的。
全局对象和静态对象是以创建它们时相反的顺序销毁的。如果程序由于调用了exit函数而终止,则不会调用静态对象的析构函数。
下面的demo演示了在几个作用域不同的存储类的CreateAndDestory的类的对象,它们的构造函数和析构函数的调用顺序。
-
#include <iostream>
-
#include <string>
-
using
namespace
std;
-
-
class Demo
-
{
-
public:
-
Demo(
int,
string);
//构造函数
-
~Demo();
//析构函数
-
private:
-
int objectID;
//ID number for object
-
string message;
//message describing object
-
};
-
-
Demo::Demo(
int ID,
string messagestring)
-
{
-
objectID = ID;
//set object’s ID number
-
message = messagestring;
//set object’s descriptive message
-
-
cout<<
“Object “<<objectID<<
” constructor runs “<<message<<
endl;
-
}
-
-
Demo::~Demo()
-
{
-
cout<<(objectID==
1 || objectID==
6 ?
“\n” :
“”);
-
-
cout<<
“Object “<<objectID <<
” destructor runs “<<message<<
endl;
-
}
-
-
void create(void);
-
-
Demo first(1,“(main 前的全局对象)”);
//在全局作用域下定义first对象。它的构造函数是在执行main中的任何语句之前调用的
-
//而它的析构函数是在已经运行了所有其他对象的析构函数之后,程序终止时调用
-
-
int _tmain(
int argc, _TCHAR* argv[])
-
{
-
cout<<
“\n main 函数开始执行:”<<
endl;
-
Demo second(2,“(main 函数中的局部自动对象)”);
-
static Demo third(3,“(main 函数中的静态局部对象)”);
-
-
create();
-
-
cout<<
“\n main 函数:继续执行”<<
endl;
-
Demo fourth(4,“(main 函数中的局部自动对象)”);
-
cout<<
“\n main 函数:执行结束”<<
endl;
-
system(
“pause”);
-
return
0;
-
}
-
-
void create(void)
-
{
-
cout<<
“\n create 函数:开始执行”<<
endl;
-
Demo fifth(5,“(cerate 函数里的本地自动对象)”);
-
static Demo sixth(6,“(cerate 函数里的本地静态对象)”);
-
Demo seventh(7,“(cerate 函数里的本地自动对象)”);
-
cout<<
“\ncerate 函数:执行结束”<<
endl;
-
}
运行结果:
Object 6 destructor runs (cerate 函数里的本地静态对象)
Object 3 destructor runs (main函数中的本地静态对象)
Object 1 destructor runs (main函数前的全局对象)
分析:
Main函数中声明了3个对象,second对象,fourth对象,是局部自动对象,而third对象是一个静态局部对象。当执行到达对象的声明位置时,才会调用这些对象的构造函数。
当执行到达mian的末尾时,首先调用fourth对象的析构函数,然后是second对象的析构函数。由于third对象是静态的,因此它会存活到程序终止。调用third对象的析构函数的时机,是在调用全局对象first的析构函数之前,但在所有其他对象被销毁之后。
Create函数声明了3个对象,其中fifth和seventh是局部自动对象,sixth是静态局部对象。当create终止时,首先调用seventh对象的析构函数,然后是fifth对象的析构函数。由于sixth对象是静态的,因此它会存活到程序终止。调用sixth对象的析构函数的时机,是在调用third和first的析构函数之前,但在所有其他对象被销毁之后。
参考资料:《c++程序员教程》 电子工业出版社 张良华 译 P259-262
</div>