C++函数实现简单的内存分配

3
我尝试理解C++中的内存分配方式:
Test other = toto();

这是完整的代码源码:
#include <iostream>

class Test
{
public:
    Test()
    {
        j = i++;
        std::cout<<"default constructor "<<j<<std::endl;
    }

    Test(const Test&)
    {
        std::cout<<"constuctor by copy "<<j<<std::endl;
    }
    Test & operator=(const Test&)
    {
        std::cout<<"operator = "<<j<<std::endl;
        return *this;
    }
    int j;
    static int i;
};

int Test::i = 0;

Test toto()
{
    Test t;
    return t;
}

int main()
{
    Test other = toto();
    std::cout<<other.j<<std::endl;
    Test another;
    return 0;
}

这段代码没有使用拷贝构造函数或重载运算符=,所以我真的不太明白它是如何工作的...

我使用的是gcc 4.7.0版本。

谢谢你的帮助 :)

Jerome


@jrok - 根据FAQ上所述,“他们的一位工程师告诉我,他们发现这种按值返回优化非常快,即使您没有开启优化编译,您也会得到优化结果”。因此显然并非完全正确。这可能因编译器而异。 - Benj
@Benj 实际上,我试过使用gcc,即使没有进行优化,也不会有复制。 - jrok
2个回答

7

格式语义:

Test other = toto();

涉及多个副本(但没有赋值)。然而,编译器允许省略所有不同的实例,从而消除了副本; 几乎所有的编译器都会进行此优化。

更具体地说,标准并未指定返回类类型的值的位置,但通常的解决方案是调用者分配空间,并将一个隐藏的指向该空间的指针传递到函数中。如果没有上述提到的优化:

Test
toto()
{
    Test t;
    return t;
}

如果执行return语句时,本地变量t将被构造,然后将复制t到指向隐藏指针的空间中。此处的优化(称为命名返回值优化或NRVO)导致编译器使用指向隐藏指针的空间来存储t,而不是在本地创建一个单独的t。(显然,在这样做时,它不会销毁t,否则在复制后会这样做。)
在声明中:
Test t = toto();

正式的语义规定编译器为类型为Test的临时变量分配空间,将该空间的地址作为隐藏指针传递给toto,然后将该临时变量复制到t中并销毁它。

这里的优化在于编译器直接将t的地址传递给toto,省略了中间的临时变量。


3
请查看返回值优化,这是一种常见的优化方式,可以避免调用构造函数。

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