博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
消除临时对象
阅读量:6243 次
发布时间:2019-06-22

本文共 2684 字,大约阅读时间需要 8 分钟。

消除临时对象

在我们的代码中,有些临时对象正在使用而我们并未察觉; 性能优化时,消除临时对象,特别是大的临时对象,对提升性能效果明显; 这里列出常见的临时对象产生的地方:

按值返回

按值返回函数结果,结果就是一个临时对象

string add(string s1,string s2){    string s3;    s3 = s1+s2;    return s3;}

解决方案: 在大多数场景下,这个临时对象可以通过按引用返回来消除;

void add(string s1,string s2,string& retvalue ){    retvalue = s1+s2;}

幸运的是,编译器通常会对按值返回做优化,将其改写为按引用返回; 但编译器做的也是非常保守的工作,仅对匿名返回临时对象做这种按引用传递; 以上函数就不会做,而以下函数,编译器会自动优化为按引用传递:

string add(string s1,string s2){    return s1+s2;}

注:返回值优化(RVO: return value Optimition):由编译器来完成将值返回转换为引用返回;

按值传递参数

按值传递参数,会有临时对象的分配

string add(string s1,string s2){    string s3;    s3 = s1+s2;    return s3;}

解决方案: 改为按引用传递(如果不希望函数内部修改,加上const修饰符)

string add(const string& s1, const string& s2){    string s3;    s3 = s1+s2;    return s3;}

类型不匹配的隐式转换

赋值操作两边不是同一类型时,如果右边可以作为作为的构造函数的参数做隐式转换,那么就会有临时对象的产生; 比如:

class Counter{public:    Counter(int i):m_nCount(i){ }    void setx(){        m_x = 8;    }private:    int m_nCount;};Counter c(5);c = 6;

首先会产生一个临时对象Counter :Counter (6); 然后赋值给s1: s1 = Counter (6);

解决方案: 尽量使用相同类型,不用编译器来自动做隐式转换: 比如: 初始化:

Counter s1(5);

赋值:

Counter s1;s1.setx(6); //如果重载了operator=函数之后,就可以直接使用s1 = 6

注:最开始这里使用string作为示例,但string重载了operator=(char*),因此以下调用不会产生临时对象,感谢@飞龙 指正;

string s1;s1 = "A";

连续的对象之间的+操作符

例:

string s3;s3 = s1+ s2;

s1+s2的中间结果需要存到一个临时对象中,然后再赋值给s3;

解决方案: 采用+=操作符,一个个的加上需要的对象:

s3 = s1; s3+= s2;

当然,第一种写法更为优雅,第二种则性能高效; 当此处不是优化关键 路径上的时候,我们还是采用第一种写法就好;

成员对象的初始化

class MyClass{    MyClass(){ m_a = "A";}    private:        string m_a;}

成员对象放在构造函数中初始化,那么必然产生一个中间的临时对象;

解决方案: 采用成员初始化列表:

class MyClass{    MyClass():m_a("A"){}    private:        string m_a;}

推荐支持

如果你觉得本文对你有所帮助,请点击以下【推荐】按钮, 让更多人阅读;

附:如何确认是否有临时对象产生?

最直观的方法是编译生产汇编文件,查看汇编代码来查看;但这对汇编语言能力有要求;另外,我们可以通过直接打印出内存地址来查看(如果是在堆中重新申请的临时对象,内存地址和之前的一般都不一样,而如果是在栈中申请的临时对象,可能重用而保持一致,这时可以在类中加入参考变量,通过打印出变量的值来检查是否生成了新的对象。)

示例:

#include 
using namespace std;class Counter{public: Counter(int i):m_nCount(i){ } void setx(){ m_x = 8; } void print(){ printf("m_nCount:%d,m_x:%d,addr:%x \n",m_nCount,m_x,this); }private: int m_nCount; int m_x;};int main(){ Counter c(5); c.setx(); c.print(); c = 6; c.print(); string s1("AAAAAA"); printf("%s,addr:%x \n",s1.data(),s1.data()); s1.assign("bbbbbb"); // 使用赋值,不会新申请空间 printf("%s,addr:%x \n",s1.data(),s1.data()); s1 = "cccccc";// 使用操作符重载函数operator=,不会新申请空间 printf("%s,addr:%x \n",s1.data(),s1.data()); s1 = string("dddddd");//用临时对象直接替换,新空间更改了 printf("%s,addr:%x \n",s1.data(),s1.data()); return 0;}

运行结果:

m_nCount:5,m_x:8,addr:22ff28m_nCount:6,m_x:-1971344528,addr:22ff28AAAAAA,addr:8117e4bbbbbb,addr:8117e4cccccc,addr:8117e4dddddd,addr:811804

Posted by: 大CC | 06AUG,2015 博客: [] Github:

转载地址:http://wepia.baihongyu.com/

你可能感兴趣的文章
【区块链之技术进阶】扒一扒某乎上面对于区块链的理解(二)
查看>>
如何从PostgreSQL源码分析哪些操作需要超级用户权限 - 阿里云rds superuser提供了哪些权限...
查看>>
用java进行面向对象编程,面向对象是什么意思
查看>>
博拉科技浅谈中国企业的智能制造之路
查看>>
[LeetCode]--29. Divide Two Integers
查看>>
php如何获取原生请求体
查看>>
java web开发 高并发处理
查看>>
PHP 高级编程之多线程(二)
查看>>
ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中)
查看>>
AFNetworking 和 ASIHTTPRequest
查看>>
Qt之自定义界面(实现无边框、可移动)
查看>>
MS SQL修改数据库名称
查看>>
【RMAN】使用RMAN duplicate复制同机数据库
查看>>
概率论快速学习03:概率公理补充
查看>>
C++ 对象的内存布局(上)
查看>>
向Java开发者介绍Scala
查看>>
【软考点点】计算机基础知识
查看>>
hdu2066一个人的旅行(多源点多汇点的最短路径问题)
查看>>
老猪带你玩转android自定义控件一——打造最简单viewpagerindicator
查看>>
【hibernate框架】性能优化之list_iterate的不同之处
查看>>