zoukankan      html  css  js  c++  java
  • 第23课 神秘的临时对象

    神秘的临时对象(上)

    有趣的问题:

    下面的程序输出什么,为什么?

     1 #include <stdio.h>
     2 
     3 class Test {
     4     int mi;
     5 public:
     6     Test(int i) {
     7         mi = i;
     8     }
     9     Test() {
    10         Test(0);
    11     }
    12     void print() {
    13         printf("mi = %d
    ", mi);
    14     }
    15 };
    16 
    17 
    18 int main()
    19 {
    20     Test t;
    21     
    22     t.print();
    23 
    24     return 0;
    25 }

    程序意图:
    在Test()中以0作为参数调用Test(int i)
    将成员变量mi的初始值设置为0
    运行结果:
    成员变量mi的值为随机值

    运行程序后,你会发现mi的值是一个随机数,为什么会出现这种情况呢?

    思考:
    构造函数是一个特殊的函数
    是否可以直接调用?
    是否可以在构造函数中调用构造函数?
    直接调用构造函数的行为是什么?

    答案:
    直接调用构造函数将产生一个临时对象
    临时对象的生命周期只有一条语句的时间
    临时对象的作用域只在一条语句中
    临时对象是C++中值得警惕的灰色地带

    神秘的临时对象(中)

    分析:

    Test() {
             Test(0);   //在此处将产生一个临时对象,临时对象的声明周期只有该行语句的生命周期,过了该条语句,这个临时对象将被析构,并且临时对象没有名字。
    没有名字意味着它的作用域仅在该行代码中,过了这行代码,它就无法被访问到了。所以从生命周期和作用域来说,这里的临时对象几乎没有半毛钱关系的。
           }
    这段代码的本意是想使用代码复用,这种思想没有错。但是如何解决临时对象这个问题呢?
    提供一种解决思路:
     1 #include <stdio.h>
     2 
     3 class Test {
     4     int mi;
     5     
     6     void init(int i)
     7     {
     8         mi = i;
     9     }
    10 public:
    11     Test(int i) {
    12         init(i);
    13     }
    14     Test() {
    15         init(0);
    16     }
    17     void print() {
    18         printf("mi = %d
    ", mi);
    19     }
    20 };
    21 
    22 
    23 int main()
    24 {
    25     Test t;
    26     
    27     t.print();
    28 
    29     return 0;
    30 }

    神秘的临时对象(下)

    编译器的行为
    现代C++编译器在不影响最终执行结果的前提下,会尽力减少临时对象的产生。

     1 #include <stdio.h>
     2 
     3 class Test
     4 {
     5     int mi;
     6 public:
     7     Test(int i)
     8     {
     9         printf("Test(int i) : %d
    ", i);
    10         mi = i;
    11     }
    12     Test(const Test& t)
    13     {
    14         printf("Test(const Test& t) : %d
    ", t.mi);
    15         mi = t.mi;
    16     }
    17     Test()
    18     {
    19         printf("Test()
    ");
    20         mi = 0;
    21     }
    22     int print()
    23     {
    24         printf("mi = %d
    ", mi);
    25     }
    26     ~Test()
    27     {
    28         printf("~Test()
    ");
    29     }
    30 };
    31 
    37 int main()
    38 {
    39     Test t(10); //等价于Test t = Test(10)-------->可以这样来解读,a.生成一个临时对象,b. 用临时对象初始化t对象。既然是这样就会涉及调用拷贝构造函数。
    //但是从编译运行结果来看,根本就没有去调用拷贝构造函数。说明编译器根本没有按照上面分析的这种思路去走,为什么呢?难道是之前我们分析的有问题?
       //因为现代的c++编译器都会减少临时对象的产生。c++编译器为了杜绝临时对象的产生,直接将Test t = Test(10)等价为了Test t = 10;
      
    t.print(); 45 return 0; 46 }
    #include <stdio.h>
    
    class Test
    {
        int mi;
    public:
        Test(int i)
        {
            printf("Test(int i) : %d
    ", i);
            mi = i;
        }
        Test(const Test& t)
        {
            printf("Test(const Test& t) : %d
    ", t.mi);
            mi = t.mi;
        }
        Test()
        {
            printf("Test()
    ");
            mi = 0;
        }
        int print()
        {
            printf("mi = %d
    ", mi);
        }
        ~Test()
        {
            printf("~Test()
    ");
        }
    };
    
    Test func()
    {
        return Test(20);
    }
    
    int main()
    {
        Test t = Test(10); // ==> Test t = 10;
        Test tt = func();  // ==> Test tt = Test(20); ==> Test tt = 20;
        
        t.print();
        tt.print();
        
        return 0;
    }

    小结:

    直接调用构造函数将产生一个临时对象
    临时对象是性能的瓶颈也是bug的来源之一
    现代C++编译器会尽力避开临时对象(前提是不影响运行结果)
    实际工程开发中需要人为的避开临时对象。

    注意:从这节课的代码来看,逻辑没有什么问题,但是就是因为临时对象的存在,使程序的执行结果产生不可思议的问题。此时就要考虑临时对象了。



  • 相关阅读:
    ORM
    优酷:exec 补充,元类,优酷构架,ORM。
    数据库 事务。
    Python操作mysql
    数据库对表操作的练习题总结。
    ACM-ICPC 2018 南京赛区网络预赛 E. AC Challenge (状压DP)
    CodeForces
    CodeForces
    AcWing 314. 低买 (线性DP)打卡
    AcWing 313. 花店橱窗 (线性DP)打卡
  • 原文地址:https://www.cnblogs.com/-glb/p/11854123.html
Copyright © 2011-2022 走看看