> 文档中心 > 关于匿名对象生命周期的讨论

关于匿名对象生命周期的讨论


关于匿名对象生命周期的讨论

😊本文为小碗里原创,CSDN首发

📅发布时间:2022/3/20

🙌欢迎大家👍点赞❤收藏✨加关注

✒本文大约1000词左右

🙏笔者水平有限,如有错误,还望告诉笔者,万分感谢!

🚩有什么问题也可在评论区一起交流哦!

🤔问题:

我在模拟实现vector的resize时我们写了这样一段代码:

void resize(size_t n, const T& val = T()){if (n < size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);//增容}while (_finish != _start + n){*_finish = val;_finish++;}}}

🤔在这里我们用到了匿名对象,并用一个变量接收,我们难免会产生这样的几点疑惑:

🔎这里使用了匿名对象传缺省值,匿名对象的生命周期不是只在当前行吗?

🔎val是T()的引用,那该行结束,T()生命周期结束,val不也应该失效吗?

🔎那为什么程序没有出错?

🔎如果T是像int,double这样的内置类型,它们也有自己的构造函数?

下面让我们来看一个例子:

class A{public:A()//构造函数{}~A()//析构函数{      //调用就会打印~A()cout << "~A()" << endl;}};int main(){A();   //const A& a = A();cout << "haha" << endl;}

运行结果

在这里插入图片描述

可以看到,程序在结束了A()所在行之后,调用了析构函数,打印了~A(),这是我们预料之中的事,因为匿名对象的生命周期在当前行,该行结束就调用析构函数.

那让我们看下一段代码~~

class A{public:A()//构造函数{}~A()//析构函数{      //调用就会打印~A()cout << "~A()" << endl;}};int main(){//A();   const A& a = A();cout << "haha" << endl;}

运行结果

在这里插入图片描述

通过观察运行结果,我们可以清晰的看到,A()是在打印完"haha"之后才调用的析构函数,这出乎了我们的预料!🤔

我们初步的得到了几个结论:
const 引用修饰的变量接收了匿名对象的值,可以延长匿名对象的生命周期至函数执行完毕!

但这还没完,下面我们把上面的代码改一改,不显式的写出构造函数,发现一件更诡异的事情~~ 🤔

class A{public:~A()//析构函数{      //调用就会打印~A()cout << "~A()" << endl;}};int main(){//A();   const A& a = A();cout << "haha" << endl;}

运行结果

在这里插入图片描述

这不由得让我们多了两个疑问:🤔

const 引用的变量接收了匿名对象的值,不是会延长其生命周期吗?为什么在结束了当前行就调用了析构函数?

程序函数最后为什么又调用了一次析构函数?

对于综上出现的各种情况,我们得出结论:

在vs环境下(我用的是vs2013)

当我们显式的写了构造函数和析构函数时,匿名对象会去调用我们自己的构造函数,编译器会认为匿名对象调用了我们自己的构造函数是赋给了匿名对象实质性内容的,如果没有const 引用接收,那自然当结束当前行就析构;但如果有const 引用接受,匿名对象有实质性内容,则编译器不会立即析构它,而会在函数出了作用域后再析构.

若不显式写构造函数和析构函数,匿名对象会去调用系统默认的构造函数,系统默认的构造函数对于内置类型不作处理,对于自定义类型会去调用其自己的构造函数,则编译器认为该匿名对象无实质性的内容,则当该行结束后就调用了析构函数析构了该匿名对象!

至于为什么最后又调用了一次析构函数,这可能是vs中的一个bug!

之后我又在Linux环境下用g++运行了此代码,结果与vs的运行结果不一样~~

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

可以看到,Linux环境下,g++的处理方式又有一些不同

在g++中,无论是否显式的写了构造函数,只要用const 引用接收匿名对象,就延长其生命周期.

本 文 到 此 结 束 , 感 谢 浏 览 本文到此结束,感谢浏览 ,