C++临时对象

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

C++临时对象

清风自在 流水潺潺   2022-06-03 我要评论

一、初探临时对象

1.问题

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

下面编写程序进行实验:

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

输出结果如下:

程序意图:

  • 在 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;
}

输出结果如下:

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

#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++ 编译器在不影响最终执行结果的前提下,会尽力减少临时对象的产生!!!

下面来看一个例子:

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

输出结果如下:

注意两点:

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

三、小结

  • 直接调用构造函数将产生一个临时对象
  • 临时对象是性能的瓶颈,也是 bug 的来源之一
  • 现代 C++ 编译器会尽力避开临时对象
  • 实际工程开发中需要人为的避开临时对象

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们