ホームページ  >  に質問  >  本文

c++ 临时变量问题?

#include <iostream>
#include <string>
using namespace std;

const string& Func()
{
    return "123";
}

int main()
{
    string s = Func();

    cout << s << endl;

    return 0;
}

const 引用不是会提升临时变量的生命期吗? 为什么返回函数内的临时变量会报错了? 我对const引用的理解哪里错了吗?

#include <iostream>
#include <string>
using namespace std;

const string& Func(const string& s)
{
    return s;
}

int main()
{
    string s = Func("123");

    cout << s << endl;

    return 0;
}

但是这段代码就可以了。 Func函数参数绑定到一个临时变量,然后返回这个临时变量的const引用,就没有问题?
why?

ringa_leeringa_lee2714日前536

全員に返信(5)返信します

  • 阿神

    阿神2017-04-17 13:34:43

    因为前者,“123”是在Func函数的内部,也就是该函数的栈上,出了函数就没有了。
    而后者“123”是在main函数的内部Func函数执行完了,控制流返回到了main函数还在。
    const引用会提高临时变量的生命周期是指,临时变量的生命周期本来只是创建该临时变量的表达式,表达式结束后,被析构,const引用将其生命周期提升到该函数结束时(如果是全局const引用变量,那自然就是提升到整个程序的生命周期),函数结束被析构,而并不会将其生命周期提升到函数外部,函数结束时,也会被析构掉的。
    const引用其实和右值引用差不多,都是都是将临时变量的生命周期从该表达式提升到函数。

    补充一点参考资料

    Whenever a reference is bound to a temporary or to a base subobject of a temporary, the lifetime of the temporary is extended to match the lifetime of the reference, with the following exceptions:

    • a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.

    • a temporary bound to a reference member in a constructor initializer list persists only until the constructor exits, not as long as the object exists. (note: such initialization is ill-formed as of DR 1696)

    • a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.(until C++14)

    • a temporary bound to a reference in the initializer used in a new-expression exists until the end of the full expression containing that new-expression, not as long as the initialized object. If the initialized object outlives the full expression, its reference member becomes a dangling reference.

    In general, the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference to which the temporary was bound, does not affect its lifetime.

    引自http://en.cppreference.com/w/cpp/language/reference_initialization

    翻译一下前两段,尤其注意加粗的字:
    无论何时一个绑定了一个临时变量或者一个临时变量的基类子对象的引用,临时变量的生命周期被扩展到与引用的生命周期相对应,除了一下几个例外:
    一个在函数的return语句中对函数的返回值的临时绑定不会被延展:它会在return语句结束时立即被销毁。这种函数总是返回一个悬垂引用

    返事
    0
  • ringa_lee

    ringa_lee2017-04-17 13:34:43

    第一段程序main方法:
    改为:
    const string s = Func();

    返事
    0
  • PHPz

    PHPz2017-04-17 13:34:43

    返回函数内定义的局部变量是未定义行为。

    const string& Func()
    {
        return "123";  // 返回定义在函数 Func 里的局部变量 "123",是未定义行为。
    }  // 变量 "123" 在此处被销毁
    
    int main()
    {
        string s = Func();  // s 指向已被销毁的变量
    
        cout << s << endl;
    
        return 0;
    }
    const string& Func(const string& s)
    {
        return s;  // 返回函数外定义的变量的引用 s
    }
    
    int main()
    {
        string s = Func("123");  // s 指向定义在函数 main 里的局部变量 "123"
    
        cout << s << endl;
    
        return 0;
    }  // 变量 "123" 在此处被销毁

    返事
    0
  • PHPz

    PHPz2017-04-17 13:34:43

    你这里const是不会提升局部变量的生命周期的,局部变量是存放在栈上,函数返回的时候相应的栈会被释放,也就是局部变量也会被销毁。你第一个程序不是局部变量声明周期的问题,const是代表不能修改的意思,但是你的s变量没有用const修饰,所以不能赋值。

    返事
    0
  • ringa_lee

    ringa_lee2017-04-17 13:34:43

    个人觉得,上面的回答都有些问题。。。
    看题主的代码:

    #include <iostream>
    #include <string>
    using namespace std;
    
    const string& Func()
    {
        return "123";
    }
    
    int main()
    {
        string s = Func();
    
        cout << s << endl;
    
        return 0;
    }

      这段代码确实会报错,但是只要把 const string& Func() 中的 & 去掉就可以了。出错的原因并不是"123"在栈中,当函数返回时出栈导致的错误。而是,"123"本身是在静态数据区的,当函数返回时实际返回的是"123"在静态数据区的地址,该地址在函数Func中是局部变量(在栈中),此时,我们使用引用类型返回该局部变量的(但是,会发生出栈操作),我们的引用变量被释放了,就会出错。但是,如果我们不使用 & ,返回的时候我们实际得到的是地址值的中间变量(函数的非引用返回值都会保存在中间变量中),此时栈操作是不影响我们获取正确的地址值的。

    返事
    0
  • キャンセル返事