C++中的临时对象
1,临时对象神秘在于不知不觉就请入程序当中,并且给程序带来了一定的问题;
2,下面的程序输出什么?为什么?
- #include <stdio.h>
- class Test
- {
- int mi;
- public:
- Test(int i)
- {
- mi = i;
- }
- Test() // 这里程序作者想要代码复用,直接调用已经构造好的函数来完成没有参数的构造函数的函数体;
- {
- Test(); // 得到临时对象,没有名字,就意味着作用域只在这个语句,过了这个语句,就没法被访问到了;这里的语句在这里没有什么作用,等价于空的语句;
- }
- void print()
- {
- printf("mi = %d\n", mi);
- }
- };
- int main()
- {
- Test t;
- t.print(); // 期望 mi 为 0;但是结果却是随机值;
- return ;
- }
3,程序意图:
1,在 Test() 中以 0 作为参数调用 Test(int i);
2,将成员变量 mi 的初始值设置为 0;
运行结果:
1,成员变量 mi 的值为随机值;
4,构造函数是一个特殊的函数:
1,是否可以直接调用?
1,给编译器主动调用的,但也可直接手工调用;
2,是否可以在构造函数中调用构造函数?
1,从编译器的编译结果来看在语法上合法;
3,直接调用构造函数的行为是什么?
1,直接调用构造函数将会产生一个临时对象;
1,是一个合法的 C++ 对象,生命期只有一条语句时间;
2,过了这个语句临时对象就会被自动析构而不复存在;
3,临时对象是没有名字的;
2,临时对象的生命周期只有一条语句;
3,临时对象的作用域只在一条语句中;
4,临时对象是 C++ 中值得警惕的灰色地带;
1,同 C 中的野指针一样必须警惕;
5,避免因代码复用调用构造函数而带来的临时对象的解决方案:
- #include <stdio.h>
- class Test {
- int mi;
- void init(int i)
- {
- mi = i;
- }
- public:
- Test(int i)
- {
- init(i);
- }
- Test()
- {
- init(); // 工程中代码复用方式
- }
- void print() {
- printf("mi = %d\n", mi);
- }
- };
- int main()
- {
- Test t;
- t.print();
- return ;
- }
6,临时对象的测试:
- #include <stdio.h>
- class Test {
- int mi;
- void init(int i)
- {
- mi = i;
- }
- public:
- Test(int i)
- {
- printf("Test(int i)\n");
- init(i);
- }
- Test()
- {
- printf("Test()\n");
- init();
- }
- void print() {
- printf("mi = %d\n", mi);
- }
- ~Test()
- {
- printf("~Test()\n");
- }
- };
- int main()
- {
- printf("main begin1\n");
- Test(); // 产生临时对象;打印 ==> Test() ~Test()
- Test(); // 产生临时对象;打印 ==> Test(int i) ~Test()
- printf("main end1\n");
- printf("main begin2\n");
- Test().print(); // 临时对象生成之后直接调用 print() 函数;这里可以通过编译,因为调用了构造函数之后呢就会产生临时对象了,通过合法的 C++ 对象加上点操作符来调用对应的成员函数是完全没有问题,完全合法的;产生临时对象;打印 ==> Test() mi = 0 ~Test()
- Test().print(); //产生临时对象;打印 ==> Test(int i) mi = 10 ~Test()
- printf("main end2\n");
- return ;
- }
1,这里仅是教育需要,向大家介绍这个知识点,在以后工程开发中,万不可这样写代码,一定要去避免临时对象的产生和使用;
7,现代 C++ 编译器在不影响最终执行结果的前提下,会尽力减少临时对象的产生;
8,神秘的临时对象编程实验:
1,证明 C++ 编译器在极力的减少临时对象的产生;
- #include <stdio.h>
- class Test
- {
- int mi;
- public:
- Test(int i)
- {
- printf("Test(int i) : %d\n", i);
- mi = i;
- }
- Test(const Test& t)
- {
- printf("Test(const Test& t) : %d\n", t.mi);
- mi = t.mi;
- }
- Test()
- {
- printf("Test()\n");
- mi = ;
- }
- int print()
- {
- printf("mi = %d\n", mi);
- }
- ~Test()
- {
- printf("~Test()\n");
- }
- };
- Test func()
- {
- return Test(); // 生成一个临时对象,函数调用结束后就立即销毁临时对象了
- }
- int main()
- {
- /*
- Test t(10); // 等价于 Test t = Test(10); 于是可解读为:1,生成临时对象 2,用临时对象初始化即将生成的 t 对象,于是必然涉及到调用拷贝构造函数,但是编译器打印的结果为 Test(int i) : 10 mi = 10 ~Test()根本没有任何拷贝构造函数的迹象产生;编译器根本没有像我们解读的上述的两个步骤执行;这是因为现代的 C++ 编译器都会尽量的减少临时对象的产生;从执行结果来看,上面的分析是没有任何错误的,只是上面的还要再调用一次拷贝构造函数,通过上面的分析,即便结果没有任何的变化,但是效率降低了,因此在这个地方 C++ 编译器为了杜绝临时对象的产生,直接将Test t = Test(10) 等价成为了 Test t = 10,这样就杜绝了临时对象的产生;杜绝临时对象的产生是因为其往往会带来性能上面的题,先生成一个临时对象调用了一次构造函数,再将临时对象通过拷贝构造函数来初始化 t,也就是说调用了两次构造函数,如果说我们极力的减少临时对象的产生,那么通过上述第二个方式等价 ,这样调用一次构造函数就可以了,少调用了一次拷贝构造函数,性能就提升了;在这个地方从性能上我们没有多大体会,但在实际的工程 开发中,构造函数里面所做的初始化工作往往是纷繁复杂的,比方说有些类的对象在初始化的时候,在调用构造函数的时候很可能打开外部设备,对设备进行初始设置等等,如果这个时候多了一次构造函数的调用,时间就消耗了;为什么要这样呢?因为 C++ 天生继承了 C 的特性,C 的一个最大特性就是高效,所以 C++ 的一个特性也是要极力的做到高效,该省掉临时对象的地方,就该省掉;
- */
- Test t = Test(); // ==> Test t = 10;
- t.print();
- Test tt = func(); // ==> Test tt = Test(20); ==> Test tt = 20; fun() 返回一个临时对象,到达了赋值符号右侧,此时临时对象里面的值会初始化 t 对象;当代的 C++ 编译器,在不影响最终结果的情况下,会极力的减少临时对象的产生;
- tt.print(); // 打印 mi = 20;
- return ;
- }
9,小结:
1,直接调用构造函数将产生一个临时对象;
2,临时对象是性能的瓶颈,也是 bug 的来源之一;;
1,C++ 中除了野指针,还有临时对象;
2,工作中,如果出现了奇怪的问题,要从这两个方面考虑;
3,现代 C++ 编译器会尽力避开临时对象;
1,避开的前提是不影响最终的执行结果;
4,实际工程开发中需要人为的避开临时对象;
1,避免调用构造函数来初始化;
2,设计函数不要引入像 fun() 函数中的临时对象;
C++中的临时对象的更多相关文章
- 认识C++中的临时对象temporary object 分类: C/C++ 2015-05-11 23:20 137人阅读 评论(0) 收藏
C++中临时对象又称无名对象.临时对象主要出现在如下场景. 1.建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象. Integer inte= Integer(5); ...
- 二十一、C++中的临时对象
思考: 构造函数是一个特殊的函数 是否可以直接调用? 是否可以在构造函数中调用构造函数? 直接调用构造函数的行为是什么? 答: 直接调用构造函数将产生一个临时对象 临时对象的生命周期只有一条语句的时间 ...
- 从汇编看c++中临时对象的析构时机
http://www.cnblogs.com/chaoguo1234/archive/2013/05/12/3074425.html c++中,临时对象一旦不需要,就会调用析构函数,释放其占有的资源: ...
- 【编程篇】C++11系列之——临时对象分析
/*C++中返回一个对象时的实现及传说中的右值——临时对象*/ 如下代码: /**********************************************/ class CStuden ...
- 与临时对象的斗争(上)ZZ
C++ 是一门以效率见长的语言(虽然近来越来越多的人“不齿”谈及效率,我深以为不然,在某一次的程序编写中不对效率锱铢必较并不意味意味着我们就不应该追求更多的更好的做法).总之吧,相比起其它语言,程序员 ...
- C++中临时对象的学习笔记
http://www.cppblog.com/besterChen/category/9573.html 所属分类: C/C++/STL/boost 在函数调用的时候,无论是参数为对象还是返回一个对 ...
- [转] C++中临时对象及返回值优化
http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...
- struts2中,OGNL访问值栈的时候查找的顺序是什么?请排序:模型对象、临时对象、固定名称的对象、Action对象
struts2中,OGNL访问值栈的时候查找的顺序是什么?请排序:模型对象.临时对象.固定名称的对象.Action对象 解答:struts2的值栈排列顺序为:1).临时对象:2).模型对象:3).Ac ...
- 转:C++中临时对象及返回值优化
http://www.cnblogs.com/xkfz007/articles/2506022.html 什么是临时对象? C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行 ...
随机推荐
- XMPP即时通讯协议使用(一)——Openfire安装
Openfire服务器安装 下载地址:https://www.igniterealtime.org/downloads/index.jsp,根据你的操作系统,选择对应的下载版本.本文选择的是openf ...
- linux性能分析工具Sysstat
- Linux学习笔记之VIM编辑器
此处根据需要,只罗列一些常用的指令和用法 五.VIM程序编辑器 Vi与vim Vi打开文件没有高亮注释,vim有,且vim是vi的高级版本 Vim默认打开文件为命令模式 i ...
- ESP8266物联网开发 一
其实学了这么多,最终目的还是在于物联网. 好吧,我们就在这个的基础上来吧,先摸索,边学边摸索. 去网上买了8266开发板... 遇到的问题: USB线的问题.从同事那随便搞了一根USB线,然后写好程序 ...
- Sass Maps的函数-map-remove($map,$key)、keywords($args)
map-remove($map,$key) map-remove($map,$key) 函数是用来删除当前 $map 中的某一个 $key,从而得到一个新的 map.其返回的值还是一个 map.他并不 ...
- Mongodb使用shell实现与javascript的动态交互
关于利用mongodb的shell执行脚本,这点在以前的文章中有点遗漏:现在在此篇博客中做个补充: 一.在命令行中传入脚本文件 定义一个javasciprt文件,名称为:script1.js,内容如下 ...
- 百度MIP技术快速入门(上)
前言 「本文假定读者已经有初级的前端开发知识,包括HTML.CSS.」 百度在一年前推出了称为 MIP(Mobile Instant Pages)的前端开发组件,主要目的是加速移动端网页的显示.MIP ...
- BZOJ2695 保护古迹
非常带劲之计算几何 写的头晕= = 就是平面图转对偶图然后最小割 由于p非常小我们枚举所有保护状态然后割一下 建图真的烦 就是把区域划分出来看一下每一个古迹点是否被小区域包含[好像也可以写点定位] 然 ...
- java 比较两个日期大小(2) 用before(), after()
调试代码,我就不整理了,记下after() before() 觉得这张图好美,从人家的博客上截的,找不到链接了
- Ubuntu Visual code安装与使用
1.直接启动软件中心,输入visual studio code,点击install即可,千万千万不要去装逼搞什么linux指令安装,死都不知道怎么死的 2.Visual code是以文件夹为工程目录的 ...