静态引用引用临时变量

时间:2015-09-15 17:54:59

标签: c++

#include <iostream>

int getID ( int k ) {
    static int& r = k;
    return r++;
}

int main()
{
    int a = getID ( 10 );
    int b = getID ( 10 );
    std::cout << "a = " << a << ", b = " << b  << std::endl;
    return 0;
}

我不明白为什么这段代码会编译。

  1. 静态引用如何引用局部变量k,它将在函数调用结束时消失。)
  2. 在第二次调用时,我们使用new重新初始化静态引用 变量。请解释一下这里发生了什么,怎么来静止 参考可以“重新定义”(我猜我不理解 方法中对静态变量的引用的含义。)。

2 个答案:

答案 0 :(得分:5)

  1. 该语言不会阻止您绑定对有限生命周期的对象的引用。这将使得很难使用参考文献。

    (它确实阻止你绑定对temporaries的引用。但是函数的参数是l值,就像局部变量一样,所以它是允许的。)

    但是,如果您编写代码,其中引用绑定到一个超出它的对象,并且您继续使用该引用,则会得到未定义的行为,不需要诊断,并且很可能是段错误。它与C中的悬空指针基本相同。

  2. 在第二次调用时,您将静态引用变量重新绑定到新临时变量。静态变量仅初始化一次第一次时间调用该函数。所有后续调用都会有效跳过该行。

  3. 如果你想要一种能够为你捕捉到这样的错误的语言,并确保你不会因为悬挂引用而被咬,你可以看看Rust。生锈编译器有一个&#34;借用检查器&#34;它将检查您的引用,而不会像许多众所周知的垃圾收集语言那样强加运行时开销。但Rust没有静态变量,因此没有直接翻译该代码;)

    在C ++中,我猜想上面的错误可能会被像Coverity这样的静态分析工具捕获,它会在扫描代码时对代码进行一些生命周期检查。但是C ++编译器不会为你做这件事,你需要使用第三方工具。

答案 1 :(得分:3)

  

静态引用如何引用局部变量k,它将在函数调用结束时消失。

完全有可能。您最终会得到一个悬空引用,这会导致未定义的行为。但static部分在这里并不重要。

  

在第二次调用时,我们使用新变量重新初始化静态引用。 [...]

不,我们没有。 static变量仅初始化一次。因此,在getID()中第二次,我们仍然指的是之前的临时k。然后我们增加,这是未定义的行为。 &#34;未定义行为的一个方面&#34;是&#34;代码看起来像是有效的。&#34;

考虑一种新类型:

struct WrappedInt {
    ~WrappedInt() {
        i = 0;
    }

    int i;
};

并重写代码以改为使用它:

int getID ( WrappedInt k ) {
    static WrappedInt& r = k;
    return r.i++;
}

在这里,我们将看到,一旦临时超出范围,并且它明确地将其值清零,当我们重新读取它(使用我们的悬空参考)时,我们将返回0

int main()
{
    int a = getID ( WrappedInt{10} );  // a == 10
    int b = getID ( WrappedInt{10} );  // b == 0 
    std::cout << "a = " << a << ", b = " << b  << std::endl;
    return 0;
}