目录
  • 一、初探临时对象
    • 1.问题
    • 2.思考
    • 3.答案
  • 二、编译器的行为
    • 三、小结

      一、初探临时对象

      1.问题

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

      C++简明分析临时对象是什么

      下面编写程序进行实验:

      #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();
          return 0;
      }

      输出结果如下:

      C++简明分析临时对象是什么

      程序意图:

      • 在 Test() 中以 0 作为参数调用 Test(int i)
      • 将成员变量 mi 的初始值设置为 0

      运行结果:

      • 成员变量 mi 的值为随机值

      2.思考

      构造函数是一个特殊的函数

      • 是否可以直接调用?
      • 是否可以在构造函数中调用构造函数?
      • 直接调用构造函数的行为是什么?

      3.答案

      • 直接调用构造函数将产生一个临时对象
      • 临时对象的生命周期只有一条语句的时间(过了这条 C++ 语句,临时对象将被析构而不复存在)
      • 临时对象的作用域只在一条语句中
      • 临时对象是 C++ 中值得警惕的灰色地带

      可以将上面代码写成这样,避免临时对象:

      #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;
      }

      输出结果如下:

      C++简明分析临时对象是什么

      再来看一个程序,深刻体会一下临时对象:

      #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(0);
          }
          void print() {
              printf("mi = %d\n", mi);
          }
          ~Test()
          {
              printf("~Test()\n");
          }
      };
      int main()
      {
          printf("main begin\n");
          Test();
          Test(10);
          printf("main end\n");
          return 0;
      }

      输出结果如下:

      C++简明分析临时对象是什么

      这个程序很好的说明了临时对象的生命周期只有一条语句的时间(过了这条 C++ 语句,临时对象将被析构而不复存在)

      二、编译器的行为

      现代 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 = 0;
          }
          int print()
          {
              printf("mi = %d\n", mi);
          }
          ~Test()
          {
              printf("~Test()\n");
          }
      };
      Test func()
      {
          return Test(20);
      }
      int main()
      {
          //Test t(10); 等价于 Test t = Test(10);
          Test t = Test(10); // ==> Test t = 10;
          Test tt = func();  // ==> Test tt = Test(20); ==> Test tt = 20;
          t.print();
          tt.print();
          return 0;
      }

      输出结果如下:

      C++简明分析临时对象是什么

      注意两点:

      • 通过输出结果可以看到【编译器并没有按照生成临时对象,再用临时对象初始化 t 对象(其中涉及调用拷贝构造函数)】的步骤,因为现代的编译器都会尽力避免临时对象的产生。
      • Test t = Test(10); 等价于 Test t = 10; 写成Test t = 10; 可以杜绝临时对象的产生。因为临时对象的产生会带来性能上的问题,Test t = Test(10); 相当于调用了两次构造函数, 而 Test t = 10; 少调用一次函数,性能得到提升。

      三、小结

      • 直接调用构造函数将产生一个临时对象
      • 临时对象是性能的瓶颈,也是 bug 的来源之一
      • 现代 C++ 编译器会尽力避开临时对象
      • 实际工程开发中需要人为的避开临时对象
      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。