C++ 将引用赋值给局部变量,如果局部变量超出范围,它是否会超出范围?

C++ 将引用赋值给局部变量,如果局部变量超出范围,它是否会超出范围?,c++,c++11,c++14,C++,C++11,C++14,首先,我认为以下行为绝对是未定义的行为: Object & foo(){ Object o; return o; } Object & ref = foo(); 但是现在假设一个函数引用了另一个存在时间更长的对象,并将这个引用赋给一个局部变量,而这个局部变量超出了范围。当局部变量被销毁时,这个对象的引用也会被销毁吗 class b { public: int val; b& round() { val += 2

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

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;
}
另一点是,我被迫编写另一个
set()const
函数并返回
const
引用

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

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

void func2(常量向量和输入、向量和输出){
int sum=输入[0]。set()+2;
}
对我来说,涉及
const
规则的事情变得非常棘手。因此,为了解决这种情况,我正在考虑将
输入[0]复制到本地
tmp
,然后进行添加

回到原来的问题:

当引用超出范围时,包含引用的局部变量是否会破坏引用本身?i、 e.
b tmp=a.set()
tmp
超出
func1
的范围时,
a
a上没有任何更改。_a[0]
a。_a[0]
也将被释放,因为
tmp
是对
a的引用。_a[0]


请原谅我的长篇大论。。。当我知道并尝试学习模板编程和C++的时候,事情变得非常复杂和困难。因此,我试图灵活地使用
set()
作为左值赋值的引用,有时我还想将其用作右值。

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

#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;
}
#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;
}
如果在函数内创建本地引用,将返回对原始对象的引用,该对象在函数离开后仍然存在。 另一方面,如果您正在执行
foo()
中的复制,那么您当然会返回一个变量的引用,该变量已超出范围,因此已被销毁

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

#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;
}
#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;
}
编辑
为了从评论中回答OP的问题,我编辑了我的答案

不能将“地址”更新为引用指向的对象。这是通过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;
}
如果要通过引用更改指针。然后引用还需要保持一个指针。见:

#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;
}

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

基本上,func1中对[0]的引用被分配给了“tmp”。我这样做是因为我担心随着tmp超出范围,a[0]会被销毁,
这句话很奇怪。首先,仅仅因为一个对象被销毁并不意味着它所复制或引用的对象也会被销毁。第二,即使你担心
tmp
被销毁会对
\u a[0]
产生影响,那你为什么要使用
tmp
,“引用被销毁了”是一个毫无意义的术语。你的程序似乎还可以,我不清楚到底是什么问题,“首先,我认为下面的肯定是UB。”事实上没有,如果从未使用
ref
。简单地绑定一个无效引用是不可取的。使用无效引用是错误的。在下一部分中,
b tmp=a.set()
复制了
b
对象,因此没有UB@GGinside讨论完全不同的问题,因为这个代码
inta=1;int&b=a;int c=2;b=c
有时初学者认为最后一个赋值改变了
b
,因此它指的是
c
,但实际上它将
c
赋值给
a
,而
b
仍然是对
a
的引用。这就是人们说你不能重新绑定推荐人的意思。嗨,a.K.,谢谢你的回答。我不知道该如何表达这个问题。让我困惑的不是返回一个局部变量的引用,它肯定超出了范围,就像你说的那样。在foo中也很清楚,如果对象被o引用,那么它就存在。我所困惑的是不返回时的情况。例如void func1(int&a){intb=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){intb=10;a=b;}当b超出范围时,a仍然保留b的值。我不确定a是b的引用还是b更新了a引用。从与John和search online的讨论中,我猜测b在本例中更新了a的引用。请更正我自己,“func1(int*a){int b=10;a=&b},那么当b超出范围时,a现在是垃圾内存。”我认为a在进入func1之前仍然是a指向的。所以我猜当函数通过指针传递时,指针类型
#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
#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