当前位置: 首页 > news >正文

新乡定制网站建设公司商城版网站建设

新乡定制网站建设公司,商城版网站建设,鹰潭网站商城建设,加强机关门户网站建设目录 暂存函数返回结果的临时对象 表达式运算过程产生的临时对象 接下来我将持续更新“深度解读《深度探索C对象模型》”系列#xff0c;敬请期待#xff0c;欢迎关注#xff01;也可以关注公众号#xff1a;iShare爱分享#xff0c;或文章末尾扫描二维码#xff0c;自…目录 暂存函数返回结果的临时对象 表达式运算过程产生的临时对象 接下来我将持续更新“深度解读《深度探索C对象模型》”系列敬请期待欢迎关注也可以关注公众号iShare爱分享或文章末尾扫描二维码自动获得推文和全部的文章列表。 所谓临时对象就是当编译器需要一个空间来暂存表达式的求值结果时临时创建的一个未命名的对象。编译器根据程序的需要可能会安插一些临时变量来支持程序的运行这些动作是在程序员不可感知的背后默默进行所以我们有必要了解编译器在背后的所作所为。这些动作有时是为了转换原来的代码的语义以保证代码能顺利地编译通过有的是为了程序运行的正确性而暂存的对象。有两种情形一般会产生临时对象一种是暂存函数调用的返回结果一种是计算表达式的过程中暂存运算结果。下面将根据这两种情况来展开分析。 暂存函数返回结果的临时对象 先看一个例子 #include cstdioclass Object { public:Object() { printf(%s, this %p\n, __PRETTY_FUNCTION__, this); }Object(const Object rhs) : i(rhs.i) {printf(%s, this %p, rhs %p\n, __PRETTY_FUNCTION__, this, rhs);}~Object() { printf(%s, this %p\n, __PRETTY_FUNCTION__, this); }Object operator(const Object rhs) {printf(%s, this %p, rhs %p\n, __PRETTY_FUNCTION__, this, rhs);i rhs.i;return *this;}int i 0; };Object operator(const Object obj1, const Object obj2) {printf(%s, obj1 %p, obj2 %p\n, __PRETTY_FUNCTION__, obj1, obj2);Object result;result.i obj1.i obj2.i;return result; }int main() {Object a;Object b;Object c a b;printf(c.i %d\n, c.i);return 0; } 编译时暂时先关闭掉优化选项加上编译选项“-fno-elide-constructors”程序的输出结果 Object::Object(), this 0x16ba673f8 // 构造a Object::Object(), this 0x16ba673f4 // 构造b Object operator(const Object , const Object ), obj1 0x16ba673f8, obj2 0x16ba673f4 Object::Object(), this 0x16ba673a4 // 构造result // 以下调用拷贝构造临时对象0x16ba673dc Object::Object(const Object ), this 0x16ba673dc, rhs 0x16ba673a4 Object::~Object(), this 0x16ba673a4 // 析构result // 以下调用拷贝构造对象c Object::Object(const Object ), this 0x16ba673e0, rhs 0x16ba673dc Object::~Object(), this 0x16ba673dc // 析构临时对象 c.i 0 Object::~Object(), this 0x16ba673e0 // 析构c Object::~Object(), this 0x16ba673f4 // 析构b Object::~Object(), this 0x16ba673f8 // 析构a 可以看到上面的程序中编译器产生了一个临时对象即地址为0x16ba673dc第6行打印。首先有一点要注意的是在operator函数里的result变量不是临时对象它是一个局部变量这是我们自己定义的具名对象而临时对象是编译器产生的。在这个程序里编译器用了一个临时对象来保存函数的运行结果它是以局部对象result为初值调用拷贝构造函数构造出来的然后编译器再次调用拷贝构造函数将它拷贝给对象c第9行打印随后这个临时对象就被释放掉了第10行打印。 是否会产生临时对象跟编译器的实现有关不同的编译器可能采取不同的策略上面的情形只是编译器可能采用的策略之一另外编译器也有可能采用另外的优化策略比如将临时对象构造到局部对象result中这样即可以减少调用一次构造函数和一次析构函数也有可能采用更激进的优化手法直接将c的地址传递给operator函数直接在函数里构造对象c优化掉局部对象result和临时对象。第三种情形即是之前文章“深度解读《深度探索C对象模型》之返回值优化”里讲过的当类中有定义了拷贝构造函数时会触发编译器启用NRV优化。我们把优化选项打开即把编译选项“-fno-elide-constructors”去掉重新编译后输出 Object::Object(), this 0x16ee6f3f8 // 构造a Object::Object(), this 0x16ee6f3f4 // 构造b Object operator(const Object , const Object ), obj1 0x16ee6f3f8, obj2 0x16ee6f3f4 Object::Object(), this 0x16ee6f3e0 // 构造c c.i 0 Object::~Object(), this 0x16ee6f3e0 // 析构c Object::~Object(), this 0x16ee6f3f4 // 析构b Object::~Object(), this 0x16ee6f3f8 // 析构a 从输出看到对象c直接在operator函数里构造了这比之前少调用了两次拷贝构造函数和两次析构函数因为不需要构造局部对象result和临时对象了。 上面的代码在启用NRV优化后临时对象就被编译器优化掉了但在另外的一种情形下临时对象却不能够被省略掉将上面代码main函数做如下的修改 // 将 Object c a b; 修改为 Object c; c a b; 当然上面的代码只是为了举例实际的代码中可能是对象c在这里定义然后在另外的地方重新给它赋值。将上面的代码修改后重新编译在同样启用优化选项的情况下程序的输出结果 Object::Object(), this 0x16f5a73f8 // 构造a Object::Object(), this 0x16f5a73f4 // 构造b Object::Object(), this 0x16f5a73e0 // 构造c Object operator(const Object , const Object ), obj1 0x16f5a73f8, obj2 0x16f5a73f4 Object::Object(), this 0x16f5a73dc // 构造临时对象 Object Object::operator(const Object ), this 0x16f5a73e0, rhs 0x16f5a73dc Object::~Object(), this 0x16f5a73dc // 析构临时对象 c.i 0 Object::~Object(), this 0x16f5a73e0 // 析构c Object::~Object(), this 0x16f5a73f4 // 析构b Object::~Object(), this 0x16f5a73f8 // 析构a 为什么说上面第5行的打印是构造临时对象而不是构造局部对象result因为如果构造的是result那么它在operator函数调用结束时就会被析构掉了而这里却是等到operator函数调用完成后才析构它说明它确实是一个临时对象关于临时对象的存活周期下面再讲。这里编译器实际上也是做了一个优化就是将临时对象构造在result对象上了这样就省略了一次拷贝构造函数和一次析构的调用。上面的代码实际上是被转换成 Object c; c a b; // 将被转换为 Object tmp a b; // 即 operator(tmp, a, b); 函数内直接构造tmp对象 c tmp; // 即 c.operator(tmp); tmp.Object::~Object(); 那么这里编译器为什么不再进一步优化一定要保留临时对象呢因为此时不能像采用NRV优化那样将对象c的地址传递给operator函数然后在operator函数内直接构造对象c因为这样做的前提是对象的空间是一块崭新的内存空间但是此时对象c已经被构造过了那就需要先调用它的析构函数因为有可能在对象c的构造函数里有申请了系统资源如果没有先释放掉这些系统资源就重新构造它就会造成系统资源的泄漏或者其他的运行错误。那么编译器是否可以将赋值语句调用赋值运算符函数operator转换为一系列的调用析构、然后再构造的语句呢如下面这样 c.Object::~Object(); // 先析构对象c c.Object::Object(a b) // 重新构造对象c 答案是这种转换所得的结果并不一定是等同的因为上述的拷贝构造函数、析构函数和拷贝赋值运算符函数operator都可以是程序员定义的编译器不能理解程序员的意图比如程序员的预期是上面的赋值语句会调用到operator函数假如他需要在operator函数里做一些事情而此时如果把它转换成调用析构加拷贝构造函数了这就违背了程序员本来的意图所以这可能是一个错误的优化行为。 表达式运算过程产生的临时对象 在表达式的运算过程中有可能也会产生临时变量比如当运算需要进行类型转换时或者是暂存子表达式的运算结果。 类型转换产生的临时变量 int main() {double d 3.14;const int ri d;return 0; } 上面的代码将会产生一个临时变量double类型的变量d会先转换成int类型的值然后暂存在一个临时变量引用ri绑定的是这一个临时变量可以来看看编译器产生的汇编代码 .LCPI0_0:.quad 0x40091eb851eb851f # double 3.1400000000000001 main: # main# 略...movsd xmm0, qword ptr [rip .LCPI0_0] # xmm0 mem[0],zeromovsd qword ptr [rbp - 16], xmm0cvttsd2si eax, qword ptr [rbp - 16]mov dword ptr [rbp - 28], eaxlea rax, [rbp - 28]mov qword ptr [rbp - 24], rax# 略... cvttsd2si是一个SSE扩展指令它的作用是取出一个64位的浮点值并截断为一个64位的整型。上面第7行代码就是将d截断为整型并保存在eax寄存器中然后第8行再截断为32位dword类型的int类型并保存在栈空间[rbp - 28]中这个即是编译器自动产生的临时变量第9、10行是取得它的地址并赋值给[rbp - 24]即ri变量的位置。为什么需要产生一个临时变量因为此处ri引用的是一个int类型的数对ri的操作应该是整型的运算但d却是一个double类型的浮点数因此为了确保ri绑定到一个整数编译器就产生了一个整型的临时变量让ri绑定到它。顺带提一下临时对象实际上是一个右值它不允许被修改所以这里的ri引用必须是const引用如果这里去掉const编译则会通不过。 暂存运算结果产生的临时对象 假如我们给上小节中例子的Object类增加一个类型转换函数 class Object { public:// 其它不变新增如下函数operator int() { return i; } }; 再假设有这样一段代码 if ( a b 10) {// do something } a b将会产生一个临时对象然后再在此临时对象之上实施int()类型转换最后再与10比较大小。看一下它对应的汇编代码 # 省略掉其它无关的代码 lea rdi, [rbp - 48] lea rsi, [rbp - 8] lea rdx, [rbp - 16] call operator(Object const, Object const) lea rdi, [rbp - 48] call Object::operator int() mov dword ptr [rbp - 52], eax # 4-byte Spill lea rdi, [rbp - 48] call Object::~Object() [base object destructor] mov eax, dword ptr [rbp - 52] # 4-byte Reload cmp eax, 10 从省略掉的代码里知道[rbp - 8]存放的是对象a[rbp - 16]存放的是对象b[rbp - 48]其实存放的就是临时对象。上面代码的第2到第5行相当于下面的伪代码 operator(tmp, a, b); 相当于operator函数的返回结果直接构造在临时对象tmp上。之后的第6到第8行是调用类型转换函数int()并将返回值eax暂存在栈空间[rbp - 52]中然后这个临时对象就销毁了。
http://www.zqtcl.cn/news/328565/

相关文章:

  • 做公众号和网站一样吗免费正能量网站下载ww
  • 使用帝国做软件下载网站源码顺义区做网站的公司
  • 网站用什么颜色成都网站建设公司服务
  • 重庆手机网站方案设计凡科建站网站怎么保存发给别人
  • 北京住房建设官方网站xampp wordpress服务器
  • 卖衣服的网站建设素材网站免费短视频
  • 萍乡网站建设哪家公司好搜索引擎推广预算
  • 如何做网站不容易被攻击网站定位策划书
  • 自己做网站去哪买服务器多说wordpress
  • 网站排名突然没有了网站开发背景图
  • 比较容易做流量的网站设计模板素材网站
  • 电商网站建设 网站定制开发兰州展柜公司网站建设
  • 临沂城乡建设管理局网站腾讯体育
  • 一个空间怎么放两个网站ps个人网站首页怎么制作
  • 云南通耀建设工程有限公司网站国际购物网站平台有哪些
  • 网站建设外包服务上海网站建设公司怎么分辨好坏
  • 建筑类企业网站模板下载微信搜索推广
  • 上海网站备案在哪里wordpress短链接关键字
  • 金诚财富网站是谁做的建站技术博客
  • 黔东南网站设计公司儿童衣服刘涛做代言那个是什么网站
  • 网站首页样式百度推广是什么工作
  • 广告手机网站制作阿里云域名 设置网站
  • 杭州市拱墅区网站建设ui培训有用么
  • 广东手机网站建设多少钱邯郸市做网站的公司
  • seo网站优化推广怎么做龙岗中心医院
  • 建网站程序智能网站建设平台
  • 建筑公司分几级资质seo入门培训
  • wap类网站上海网站建设免费推
  • 网站建设哪家好公司建设银行网站怎么登陆不
  • 关于建设网站的需求wordpress不能发布文章