C++ 通过引用传递棘手的情况

C++ pass by reference tricky situation

我想弄清楚在用于引用此对象的变量从堆栈中删除后,临时对象“右值”会发生什么情况。

代码示例:

#include<iostream>
using namespace std;

class Base
{
  private:
  int &ref;
  public:
  Base(int &passed):ref(passed)
  {
    cout << "Value is " << ref << endl;
  }
  int getvalue(){
    return ref;
  }
};

int main()
{
  Base *myobject;
    {
    int ref=10;
    myobject = new Base (ref);
    ref++;
    }                           //ref delete from stack. 
  cout << myobject->getvalue() << endl;
  return 0;
}

我希望第二个输出(第二个 cout)给我一些随机垃圾,因为 ref 已删除但我得到的是 ref 当前状态的值,我想知道 ref 被删除后发生了什么,这纯粹是运气吗?或者 myobject.ref 存储了他构造的 ref 的值?

编辑:添加可视化工具来支持我的观点,最终 myobject.ref 无处可去 C++ visualizer

is it pure luck?

是的。引用所指向的对象的生命周期结束于 ref++;.

之后的 }

之后尝试读取对象的值会导致未定义的行为。未定义的行为意味着您无法保证程序将如何运行,这包括它看似有效的可能性,但另一个 运行 或程序的编译可能会给您不同的结果。

实际上,销毁一个 int 对象不需要执行任何机器指令,如果对象在函数的堆栈上分配了 space,那么该值很有可能将只从 int 对象所在的位置读取,无论它的最后一个值是什么。编译器不需要覆盖它。

但是如果您在启用优化的情况下进行编译,您会看到未定义的行为在起作用。例如,在 x86_64 上带有 -O2 的 GCC 11.2 会产生输出

Value is 10
10

但没有优化标志它会产生

Value is 10
11

(参见 https://godbolt.org/z/EqKG83c18)。

但未定义的行为并不仅仅意味着打印的值是未指定的。整个程序没有行为保证,它也可能不输出任何内容或垃圾文本或其他任何内容。

来自Reference declaration documentation

it is possible to create a program where the lifetime of the referred-to object ends, but the reference remains accessible (dangling). Accessing such a reference is undefined behavior.

在您的程序中,referred-to 对象 refref++ 之后的 } 结束。这意味着 ref 数据成员现在是一个 悬空引用 。根据上面引用的声明,访问该引用(调用 getValue 时所做的)是 不安全行为.

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior.

所以您看到(也许看到)的输出是未定义行为的结果。正如我所说,不要依赖具有 UB 的程序的输出。比如同一个程序的输出是不同的here and here.

因此,使程序正确的第一步是删除 UB。 然后并且只有那时你可以开始对程序的输出进行推理。


1有关未定义行为的技术上更准确的定义,请参阅 this 其中提到:没有对程序行为的限制.