神秘的临时对象(十八)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了神秘的临时对象(十八)相关的知识,希望对你有一定的参考价值。

        我们在程序中不可避免的会遇到临时变量,那么在 C++ 中也会不可避免的会遇到临时对象。我们以代码为例来进行分析

#include <stdio.h>

class Test
{
private:
    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,最后想要打印它的值。我们来看看编译结果

技术分享图片

        我们看到打印的是一个随机数,并不是我们所期望的 0。那这到底是怎么回事呢?那么我们想下:构造函数既然是一个特殊的函数。那么它是否可以直接调用呢?是否可以在构造函数中调用构造函数呢?直接调用构造函数的行为是什么?我们就直接说答案了。直接调用构造函数将产生一个临时对象,临时对象的生命周期只有一条语句的时间,临时对象的作用域只在一条语句中,临时对象是 C++ 中值得警惕的灰色地带!上面那个程序也就是说第 15 行的调用的构造函数会产生临时对象,它的生命周期只有那一行的时间,所以在后面我们打印出来的才会是一个随机值。

        下面我们在上面程序的基础上进行修改,代码如下

#include <stdio.h>

class Test
{
private:
    int mi;
    
    void init(int i)
    {
        mi = i;
    }
public:
    Test()
    {
        init(0);
    }
    
    void print()
    {
        printf("mi = %d\n", mi);
    }
};

int main()
{
    Test t;
    
    t.print();
    
    return 0;
}

        我们再次编译看看结果

技术分享图片

        这次我们看到它按照我们所想要的初始化为 0 了。现代的 C++ 编译器在不影响最终执行结果的前提下,会尽力减少临时对象的产生!!!

        我们再来看一个示例代码

#include <stdio.h>

class Test
{
private:
    int mi;
public:
    Test(int i)
    {
        mi = i;
        printf("Test(int i): %d\n", mi);
    }
    Test(const Test& obj)
    {
        mi = obj.mi;
        printf("Test(const Test& obj): %d\n", mi);
    }
    Test()
    {
        mi = 0;
        printf("Test()\n");
    }
    void print()
    {
        printf("mi = %d\n", mi);
    }
    ~Test()
    {
        printf("~Test()\n");
    }
};

Test func()
{
    return Test(50);
}

int main()
{
    Test t = Test(10);
    Test tt = func();
    
    t.print();
    tt.print();
    
    return 0;
}

        我们看到在第 40 行定义了 Test 对象 t,并将它初始化为 10。按照我们想的它在这块先是生成一个临时对象初始化为 10 并将这个临时对象赋值给对象 t。所以这块可能会牵扯到拷贝构造函数。第 41 行也是这样的,先是在 func 函数中生成一个临时对象 Test(50),并将它赋值给对象 tt。所以也会牵扯到拷贝构造函数。那么我们来编译下看看结果

技术分享图片

        我们看到并没有打印出拷贝构造函数的身影。再回想下我们之前讲的,现代编译器已经大大优化了,尽量会避免临时对象的产生。那么第 40 行将会等价于 Test t(10),第 41 行将会等价于 Test tt = Test(50) ==> Test tt(50);所以这样也就能解释清楚了,它确实没牵扯到拷贝构造函数。我们得随时注意 C++ 中的临时对象,因为它将会导致一些莫名其妙的结果。通过对临时对象的学习,总结如下:1、直接调用构造函数将产生一个临时对象,临时对象是性能的瓶颈,也是 bug 的来源之一;2、现代 C++ 编译器会尽力避开临时对象,实际工程开发中需要人为的避开临时对象。


        欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083

以上是关于神秘的临时对象(十八)的主要内容,如果未能解决你的问题,请参考以下文章

第23课.神秘的临时对象

带有神秘附加字符的 Javascript Date getTime() 代码片段

ViewPager 片段之间的神秘分隔线?

神经网络:神秘的ReLu

我在哪里更改此 Python 代码片段以将临时文件保存在 tmp 文件夹中?

第十八章 用于大型程序的工具