C++11中的构造函数

3

我希望了解更多关于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。Test 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()内存?还是其他什么事情发生了?谢谢!


请查看返回值优化 - juanchopanza
哦,我明白了,这是编译器优化。它通过执行某些操作来绕过复制构造函数,这个操作与调用析构函数相同,但不需要调用析构函数。很酷,但奇怪的是,在调试时我看不到它!我只是想确保这里没有什么可疑的事情发生,比如它调用了一些默认的、可能不那么高效的构造函数,而我没有考虑到。非常感谢您提供的链接。这基本上回答了我的问题。 - user2045279
请注意,这不是普通的编译器优化:它并不是“相同的”。RVO(以及一般的复制省略)允许改变程序的行为。 - juanchopanza
非常正确,因为它在构造函数中没有调用我的输出语句... 就像在我的预期程序中一样。话虽如此,我想构造应该是相同的。也就是说,我可以假设从构造函数(tempVar)获取的对象已经按照应该的方式构造好了。 - user2045279
没错,发生的情况是对象被“原地”构造,避免了复制。 - juanchopanza
1个回答

4

= 在声明中的意思是“隐式构造”。它不是“调用 operator= ”。

C++ 中有一个概念叫做省略(elision)。省略意味着“将两个或多个变量变成同一个东西”。编译器可以根据一定的规则来使用它,与其他优化不同的是,即使存在副作用,它也是合法的。

如果您用相同类型的未命名临时变量初始化变量,则编译器可以省略该临时变量。如果您从函数中返回局部变量,则在某些情况下,它可以省略为未命名的返回值。对于未命名的临时变量,也是如此。NRVO 和 RVO 就是您需要了解的关键字。

在许多情况下,如果由于技术原因或编译器限制而失败,省略会自动转换为 move。然而,显式的 move 会阻止省略。因此,了解这些规则对于创新性的最佳代码非常重要。


这进一步推动了关于juanchopanza所谈论的讨论。我感谢您的回复,非常感谢。 - user2045279

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接