目录
- 一、初探临时对象
 - 1.问题
 - 2.思考
 - 3.答案
 - 二、编译器的行为
 - 三、小结
 
一、初探临时对象
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++ 编译器会尽力避开临时对象
 - 实际工程开发中需要人为的避开临时对象
 
	声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
		
评论(0)