分配对局部变量的引用,如果局部变量超出范围,它会超出范围吗?

Posted

技术标签:

【中文标题】分配对局部变量的引用,如果局部变量超出范围,它会超出范围吗?【英文标题】:Assigning reference to local variable, would it go out of scope if the local variable is out of scope? 【发布时间】:2020-06-24 05:18:48 【问题描述】:

首先我认为以下绝对是未定义的行为:

Object & foo()
    Object o;
    return o;


Object & ref = foo();

但是现在假设一个函数引用另一个将存在更长时间的对象,并将该引用分配给局部变量,并且局部变量超出范围。这个对象的引用是否也会随着局部变量的销毁而被销毁?

class b 
public:
    int val;
    
    b& round() 
        val += 2;
        return *this;
    

    int operator+(int i) const
       return val + i;
    
;

template<typename T>
class A 
public:
    typedef typename std::vector<b>::const_reference const_reference;
    typedef typename std::vector<b>::reference reference;

    vector<b> _a;
    A() 
        _a.resize(10);
    

    inline const_reference set() const 
         return  _a.at(0);
    

    inline reference set()  
        return  _a.at(0).round();
    
;

void func1(A<int>& a) 
    b tmp = a.set();
    tmp.val = tmp.val + 2;
    cout << a._a[0].val << endl;


int main() 
    A<int> a;
    a.set();
    func1(a);
    cout << a._a[0].val << endl;

_a[0] 的引用基本上分配给func1 中的tmp

我这样做是因为我担心_a[0] 会因为tmp 超出范围而被破坏,但是我想返回一个引用以便我可以将它用作左值,将右值直接分配给它,喜欢做

a.set() = 1;

还有一点是我不得不写另一个set() const函数并返回一个const引用。

我不得不在另一个set 中编写const 关键字,因为在另一个函数中,例如func2,我将输入作为const vector 引用传递,但我的用法并没有修改它。但是,如果没有 set() const 方法,编译器会在调用 func2(输入、输出)时出错。

没有匹配函数调用'const vector类型的对象。参数的类型为const,但方法未标记为const。

void func2(const vector<A>& input, vector<A>& output) 
    int sum = input[0].set() +2;

对我来说,涉及const 规则的事情变得非常棘手。所以为了解决这种情况,我想先把input[0].set()复制到本地的tmp,再做加法。

所以回到原来的问题:

包含引用的局部变量会在超出范围时破坏引用本身吗?即b tmp = a.set();tmp 超出func1 的范围时,aa._a[0]a._a[0] 上的任何更改也将被释放,因为tmp 是对a._a[0] 的引用?

请原谅我太冗长了...当我知道并尝试学习模板编程和 C++ 的常量时,事情变得非常复杂和艰难...所以我试图灵活地有时使用@987654349 @ 作为左值赋值的参考,有时我也想将它用作右值。

【问题讨论】:

Basically reference to _a[0] is assigned to 'tmp' in func1. I am doing it this way because I have concerns that _a[0] would be destroed as tmp goes out of scope, 这个说法很好奇。首先,仅仅因为一个对象被破坏并不意味着它被复制或引用的对象也被破坏。其次,即使您担心tmp 被销毁会对_a[0] 产生影响,那您为什么还要使用tmp “引用被破坏”是无意义的术语 你的程序看起来不错,我不清楚问题到底是什么 "首先我认为下面肯定是 UB。" 实际上不是,如果从未使用过 ref。简单地绑定一个无效的引用不是 UB。 使用无效的引用是 UB。在下一部分,b tmp = a.set(); 复制了 b 对象,因此没有 UB。 @GGinside 那是在谈论一个完全不同的问题,鉴于此代码int a = 1; int&amp; b = a; int c = 2; b = c;,有时初学者认为最后一个分配更改了b,因此它引用了c,但实际上它分配了@987654360 @ 到 ab 仍然作为对 a 的引用。当人们说你不能重新绑定引用时,这意味着什么。 【参考方案1】:

这取决于函数中的局部变量是定义为变量还是引用。请参阅以下示例:

#include <iostream>

class Object
public:
  Object();
  ~Object();
;

Object & foo(Object & obj)
    Object& o = obj;
    std::cout << &o << std::endl;
    return o;


Object & foo2(Object & obj)
    Object o = obj;
    std::cout << &o << std::endl;
    return o;


int main() 
  Object obj;
  std::cout << &obj << std::endl;
  Object & ref = foo(obj);
  std::cout << &ref << std::endl;
  Object & ref2 = foo2(obj);
  std::cout << &ref2 << std::endl;

导致以下输出:

# g++ -o main .\main.cpp && ./main
0x61feb7
0x61feb7
0x61feb7
0x61fe8f
0

如果您在函数内部创建本地引用,您将返回对原始对象的引用,该引用在函数离开后仍然存在。 另一方面,如果您正在像foo() 那样进行复制,那么您当然会返回一个已超出范围并因此已被销毁的变量的引用。

如果您想修改现有对象,您可以使用本地参考。但是如果你想创建一个副本并修改它的值,你最好通过移动语义返回它。但请确保真正使用移动语义而不是复制构造函数。请参阅以下示例:

#include <iostream>

class Object
public:
  Object();
  ~Object();
;

Object  foo(Object & obj)
    Object o = obj;
    std::cout << &o << std::endl;
    return o;


int main() 
  Object obj;
  std::cout << &obj << std::endl;
  Object ref = foo(obj); // move semantic
  std::cout << &ref << std::endl;
  Object ref2;
  ref2 = foo(obj); // copy constructor
  std::cout << &ref2 << std::endl;

输出如下

# g++ -std=c++11 -o main .\helloWorld.cpp && ./main
0x61febe
0x61febd
0x61febd
0x61febf
0x61febc

编辑 为了回答来自 cmets 的 OP 问题,我编辑了我的答案。

您不能将“地址”更新为引用指向的对象。这是 C++ 的设计。见:Why are references not reseatable in C++

您只能更新引用指向的对象的值。见:

#include <iostream>

void foo(int& a)
  int b = 20;
  int&c = b;
  std::cout << "Address of C " << &c << " with " << c << std::endl;
  a = c; // does not copy the addres of C into a, but it's value
  std::cout << "Address of a " << &a << " with " << a << std::endl;


int main() 
  int a=10;;
  std::cout << "Address of a " << &a << " with " << a << std::endl;
  foo(a);
  std::cout << "Address of a " << &a << " with " << a << std::endl;

输出:

Address of a 0x61febc with 10
Address of C 0x61fe88 with 20
Address of a 0x61febc with 20
Address of a 0x61febc with 20

如果您想通过引用更改指针。那么refrence也需要持有一个指针。见:

#include <iostream>

void foo(int*& a)
  int b = 20;
  int* c = &b;
  std::cout << "Address of c " << &c << " pointing to " << c  << " with " << *c << std::endl;
  a = c; // copys the address stored in C into a. Now you have a possible memory leak.
  std::cout << "Address of a " << &c << " pointing to " << c  << " with " << *c << std::endl;


int main() 
  int * a = new(int);
  *a = 10;
  std::cout << "Address of a " << &a << " pointing to " << a << " with " << *a << std::endl;
  foo(a);
  std::cout << "Address of a " << &a << " pointing to " << a  << " with " << *a << std::endl;

这会导致以下输出:

Address of a 0x61febc pointing to 0x10d1738 with 10
Address of c 0x61fe88 pointing to 0x61fe8c with 20
Address of a 0x61fe88 pointing to 0x61fe8c with 20
Address of a 0x61febc pointing to 0x61fe8c with 17635044 // since b got destroyed the address a holds does not contain the expected value

但你真的不应该做后一个。这很可能会导致内存泄漏(至少)、未定义的行为或打开黑洞。

【讨论】:

您好 A.K.,感谢您的回答。我不知道如何在这个问题上表达清楚。让我感到困惑的不是返回一个肯定超出范围的局部变量的引用,就像你说的 foo2。在 foo 中也很清楚,如果对象被 o 引用,则该对象存在。我感到困惑的是不返回时的情况。例如 void func1(int& a) int b = 10; a = b;现在当 b 超出范围时,a 会发生什么?从我的程序中,我看到 a 仍然有 b 的值,假设 a 为 0,现在在 func1 之后 a 为 10。 我很困惑,因为如果 func1(int* a) int b = 10; a = &b,那么当 b 超出范围时,a 现在是垃圾内存。但似乎当它是一个参考案例时, func1(int& a) int b = 10; a = b; 当 b 超出范围时,a 仍然保留 b 的值。而且我不确定 a 是 b 的引用还是 b 更新了 a 引用。从与 John 的讨论和在线搜索中,我猜测在这种情况下 b 会更新 a 的引用。 更正自己,“func1(int* a) int b = 10; a = &b,那么当b超出范围时,a现在是垃圾内存。”我认为a仍然是什么a 在它进入 func1 之前指向。所以我猜当有一个函数通过指针传递时,指针类型值被复制,这意味着对地址的更新不是更新a的原始地址,而是本地复制a的地址。参考案例让我感到困惑,因为我认为a会成为b的参考。因此,当 b 完成时,a 完成。但是情况是a被b更新,而不是成为b的引用,这对我来说其实很有趣。 这就是我所说的让我感到困惑的内容 void func2(int& a) int b = 20; int&c = b; a = c;当 b 超出范围时,使用 c 的值而不是引用更新 a。 func1(int& a) int b = 10; a = b; 与 func1(int* a) int b = 10; 不一样a = &b。它更像 func1(int* a) int b = 10; *a = b;。 C++ 中不允许(按设计)更改引用。见:***.com/questions/728233/…

以上是关于分配对局部变量的引用,如果局部变量超出范围,它会超出范围吗?的主要内容,如果未能解决你的问题,请参考以下文章

最终的局部变量不能分配,不能分配给非最终变量

局部变量和全局变量

Python 局部变量 与全局变量

java语言中,类的成员变量分配在哪个内存区?

简述static关键字void与void *(void指针)函数指针

(十六)python 3 全局变量局部变量