我试图学习有关c++的更多信息,但我对编译器的工作感到有些困惑。我编写了以下文件,并附有注释,详细说明了会发生什么情况:

Test getTest()
{
    return Test(100, string("testing..."));
}

int main()
{
    // These two will call the initializer constructor...
    Test t1(5, string("hello"));
    Test t2(10, string("goodbye"));

    // This will not call operator=. This will call the copy constructor!
    Test t3 = t1;

    // This will call operator=(Test&)
    t3 = t2;

    // This will call operator=(Test&&) because rhs is an rvalue
    //   We will swap the resources in this operator= so that when getTest()
    //   deletes its resources, it will actually be deleting t3's old resources.
    //   Likewise, t3 will get getTest()'s resources.
    t3 = getTest();


    // I don't know what this is doing, but I know it's not calling the destructor.
    //   I beleive that the memory of t4 is simply what was returned by getTest().
    // Likewise with t5.
    Test t4(getTest());
    Test* t5 = new Test(getTest());

    Test t6(t4);

    return 0;
}

看来t4和t5没有输入任何构造函数,实际上只是在使用getTest()分配的内存。我以为会发生的是t4会进入rValue复制构造函数:Test(const Test && rhs),但是即使它的参数是rValue也不会。测试t4(getTest())不调用任何析构函数,这就是为什么我认为t4只是在获取内存。 t6确实调用了复制构造函数。

我在Visual Studio 2013中查看了汇编代码,发现以下内容:
    Test t4(getTest());
00F59B8C  push        8
00F59B8E  lea         ecx,[t4]
00F59B91  call        Test::__autoclassinit2 (0F51285h)
00F59B96  lea         eax,[t4]
00F59B99  push        eax
00F59B9A  call        getTest (0F51456h)
00F59B9F  add         esp,4
00F59BA2  mov         byte ptr [ebp-4],8

因此,看起来它调用了称为autoclassinit2的东西,然后从getTest获取内存,最后将其存储在t4中?

所以我想我的问题是:这是否只是编译器优化,用于直接将getTest()中的构造函数的内存提供给t4?而不是说:1.在getTest()中构造2.调用rVal复制构造函数3.销毁getTest()内存?还是这里发生了其他事情?谢谢!

最佳答案

声明中的=表示“请隐式构造”(基本上),而不是“call operator =”。

C++有一个称为省略的概念。省略的意思是“使两个或多个变量相同”。关于何时可以进行编译有一些规则。与其他优化不同,即使存在副作用也是合法的。

如果使用相同类型的未命名临时变量初始化变量,则编译器可以忽略该临时变量。如果从函数返回局部变量,则在某些情况下可以将其省略为未命名的返回值。同名的临时姓名未还。 NRVO和RVO是您想要的关键字。

在许多情况下,如果省略是由于技术原因或编译器限制而失败,则move会隐式出现。但是,显式的move会阻止省略。因此,了解规则对于优化边缘代码很重要。

09-15 22:09