对象的构造顺序、销毁、临时对象
对象的构造顺序
对于局部对象
当程序执行流到达对象的定义语句时进行构造
堆对象
- 当程序执行流到达new语句时创建对象
- 使用new创建对象将自动触发构造函数的调用
对于全局对象
- 对象的构造顺序是不正确的
- 不同的编译器使用不同的规则确定构造顺序
小结
- 局部对象的构造顺序依赖于程序的执行流
- 堆对象的构造顺序依赖于new的使用顺序
- 全局对象的构造顺序是不确定的
对象的销毁
- 一般而言,需要销毁的对象都应该做清理
- 解决方案
- 为每个类都提供一个public的free函数
- 对象不再需要时立即调用free函数进行清理
- 存在的问题
- free只是一个普通的函数,必须显示的调用
- 对象的销毁前没有做清理,很可能造成资源泄漏
析构函数
-
C++的类中可以定义一个特殊的清理函数
- 这个特殊的清理函数叫做析构函数
- 析构函数的功能与构造函数相反
-
定义:
~ClassName()
- 析构函数没有参数也没有返回值类型声明
- 析构函数在对象销毁时自动被调用
-
析构函数的定义标准
当类中自定义了构造函数,并且构造函数中使用了系统资源,则需要自定义析构函数
小结
- 析构函数是对象销毁时进行清理的特殊函数
- 析构函数在对象销毁时自动调用
- 析构函数是对象释放系统资源的保障
临时对象
- 直接构造函数产生一个临时对象
- 临时对象的生命周期只有一条语句的时间
- 临时对象的作用域只在一条语句中
- 临时对象是C++中值得警惕的灰色地带
#include <stdio.h>
class Test
{
int mi;
public:
Test(int i)
{
mi = i;
}
Test()
{
Test(0);//调用用参数的构造函数,产成了一个临时对象
}
void print()
{
printf("mi = %d",mi);
}
};
int main(int argc, char const *argv[])
{
Test t;
t.print();
return 0;
}
// 期望目标是:0
// 运行结果是:随机数
// 产生的结果是:在其中构造函数的相互调用产生了临时对象
对于上面产生临时对象的问题进行处理,设置一个私有的函数,进行赋值操作
#include <stdio.h>
class Test
{
int mi;
void init(int i)
{
mi = i;
}
public:
Test(int i)
{
init(i);
}
Test()
{
init(0);
}
void print()
{
printf("mi = %d",mi);
}
};
int main(int argc, char const *argv[])
{
Test t;
t.print();
return 0;
}
这样会避免在构造函数中调用构造函数,从而避免了临时对象的产生
小结
- 直接调用构造函数将会产生一个临时对象
- 临时对象是性能的瓶颈,也是Bug的来源之一
- 现代C++编译器会尽力避免临时对象
- 实际开发中应该避开临时对象