c++临时对象详解

1,临时对象神秘在于不知不觉就请入程序当中,并且给程序带来了一定的问题;

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

  #include <stdio.h>
 
  class Test
  {
      int mi;
  public:
      Test(int i)
      {
          mi = i;
     }
     
     Test()  // 这里程序作者想要代码复用,直接调用已经构造好的函数来完成没有参数的构造函数的函数体;
     {
         Test(0);  // 得到临时对象,没有名字,就意味着作用域只在这个语句,过了这个语句,就没法被访问到了;这里的语句在这里没有什么作用,等价于空的语句;
     }
     
     void print()
     {
        printf("mi = %d\n", mi);
     }
 };
 
 int main()
 {
      Test t;
    
    t.print();  // 期望 mi 为 0;但是结果却是随机值;

     return 0;
 }

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(0);  // 工程中代码复用方式
    }

    void print() {
        printf("mi = %d\n", mi);
    }
};


int main()
{
    Test t;

    t.print();

    return 0;
}

6,临时对象的测试:

 1 #include <stdio.h>
 2 
 3 class Test {
 4     int mi;
 5     
 6     void init(int i)
 7     {
 8         mi = i;
 9     }
10     
11 public:
12     Test(int i) 
13     {
14         printf("Test(int i)\n");
15         init(i);  
16     }
17     
18     Test() 
19     {
20         printf("Test()\n");
21         init(0);  
22     }
23     
24     void print() {
25         printf("mi = %d\n", mi);
26     }
27     
28     ~Test()
29     {
30         printf("~Test()\n");
31     }
32 };
33 
34 
35 int main()
36 {
37     printf("main begin1\n");
38     
39     Test();  // 产生临时对象;打印 ==> Test() ~Test()
40     
41     Test(10);  // 产生临时对象;打印 ==> Test(int i) ~Test()
42     
43     printf("main end1\n");
44     
45     printf("main begin2\n");
46     
47     Test().print();  // 临时对象生成之后直接调用 print() 函数;这里可以通过编译,因为调用了构造函数之后呢就会产生临时对象了,通过合法的 C++ 对象加上点操作符来调用对应的成员函数是完全没有问题,完全合法的;产生临时对象;打印 ==> Test()    mi = 0    ~Test()
48     Test(10).print(); //产生临时对象;打印 ==> Test(int i) mi = 10 ~Test() 
49     
50     printf("main end2\n");
51 
52     return 0;
53 }
1,这里仅是教育需要,向大家介绍这个知识点,在以后工程开发中,万不可这样写代码,一定要去避免临时对象的产生和使用;

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

8,神秘的临时对象编程实验:

1,证明 C++ 编译器在极力的减少临时对象的产生;

```cpp
 1 #include <stdio.h>
 2 
 3 class Test
 4 {
 5     int mi;
 6     
 7 public:
 8     Test(int i)
 9     {
10         printf("Test(int i) : %d\n", i);
11         mi = i;
12     }
13     
14     Test(const Test& t)
15     {
16         printf("Test(const Test& t) : %d\n", t.mi);
17         mi = t.mi;
18     }
19     
20     Test()
21     {
22         printf("Test()\n");
23         mi = 0;
24     }
25     
26     int print()
27     {
28         printf("mi = %d\n", mi);
29     }
30     
31     ~Test()
32     {
33         printf("~Test()\n");
34     }
35 };
36 
37 Test func()
38 {
39     return Test(20);  // 生成一个临时对象,函数调用结束后就立即销毁临时对象了
40 }
41 
42 int main()
43 {
44 /*

```cpp
45     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++ 的一个特性也是要极力的做到高效,该省掉临时对象的地方,就该省掉;               
46 */    
47     Test t = Test(10); // ==> Test t = 10;
48     
49     t.print();
50     
51     Test tt = func();  // ==> Test tt = Test(20); ==> Test tt = 20; fun() 返回一个临时对象,到达了赋值符号右侧,此时临时对象里面的值会初始化 t 对象;当代的 C++ 编译器,在不影响最终结果的情况下,会极力的减少临时对象的产生;
52                    
53     tt.print();  // 打印 mi = 20;
54     
55     return 0;
56 }

9,小结:

1,直接调用构造函数将产生一个临时对象; 2,临时对象是性能的瓶颈,也是 bug 的来源之一;; 1,C++ 中除了野指针,还有临时对象; 2,工作中,如果出现了奇怪的问题,要从这两个方面考虑; 3,现代 C++ 编译器会尽力避开临时对象; 1,避开的前提是不影响最终的执行结果; 4,实际工程开发中需要人为的避开临时对象; 1,避免调用构造函数来初始化; 2,设计函数不要引入像 fun() 函数中的临时对象;


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

键盘小王子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值