在 C++ 中,为什么当我将一个引用变量重新分配给另一个引用变量时,它会创建该变量的副本?

In C++ why is it when I reassign a reference variable to a different reference variable it creates a copy of that variable instead?

struct configfs
{
    int & foo;
    configfs(int & foo_) : foo(foo_) {}

    void set_foo(int & foo_)
    {
        foo = foo_;
    }

};

int main() { 
    int a = 1;
    configfs conf(a);
    a = 3;
    std::cout << conf.foo; // 3
    std::cout << a;      // 3

    int b = 2;
    conf.set_foo(b);
    b = 9;
    std::cout << conf.foo; // 2
    std::cout << b;        // 9

    return 0;
}

当我用 int a 初始化 configfs 然后在主函数中改变 a 它也会改变 configfs.foo 。但是当我将 configfs.foo 重新分配给一个新的引用并在主函数中更改该引用时,行为是不同的。为什么?有没有办法让我 运行 conf.set_foo(b) 然后在主作用域中改变 b 时,它也会改变 conf.foo

引用只能在初始化期间“赋值”。它们不能像指针那样被“重定向”。初始化后,引用所指的内存保持不变,任何使用 = 的赋值都会导致对引用所指的内存调用赋值运算符。这就是它在 C++ 标准中指定的方式。

您可以使用自定义类型重写示例,并使用赋值运算符打印有关调用的信息:

template<class T>
struct configfs
{
    T& foo;
    configfs(T& foo_) : foo(foo_) {}

    void set_foo(T& foo_)
    {
        foo = foo_; // this uses the assignment operator assigning to the variable foo is an alias for
    }

};

// Type wrapping the int and printing out the operations applied
struct TestWrapper
{
    TestWrapper(int value)
        : value(value)
    {
        std::cout << "TestWrapper::TestWrapper(" << value << ")\n";
    }

    TestWrapper(TestWrapper const& other)
        : value(other.value)
    {
        std::cout << "TestWrapper::TestWrapper(TestWrapper{" << other.value << "})\n";
    }

    TestWrapper& operator=(TestWrapper const& other)
    {
        std::cout << "TestWrapper::operator=(TestWrapper{" << other.value << "})\n";
        value = other.value;
        return *this;
    }

    int value;
};

std::ostream& operator<<(std::ostream& s, TestWrapper const& val)
{
    s << val.value;
    return s;
}

int main() {
    TestWrapper a = 1;
    configfs<TestWrapper> conf(a);
    a = 3;
    std::cout << conf.foo << '\n'; // 3
    std::cout << a << '\n';      // 3

    TestWrapper b = 2;
    conf.set_foo(b); // TestWrapper::operator=(TestWrapper{2})
    b = 9;
    std::cout << conf.foo << '\n'; // 2
    std::cout << b << '\n';        // 9

    return 0;
}

当你写道:

conf.set_foo(b);

发生以下情况:

  1. 在对象conf.

    上调用了成员函数set_foo
  2. 此外,名为foo_ 的引用参数绑定了名为b 的参数。也就是说,b 通过引用传递。

  3. 接下来遇到语句foo = foo_;。这是一个赋值语句而不是初始化。它的作用是将参数 foo_ 引用的值 赋给 数据成员 foo 引用的对象(这不过是 a 这里)。您可以通过在调用 set_foo 之后添加 std::cout<<a;confirm this,如下所示:

int b = 2;
conf.set_foo(b);
std::cout<<a<<std::endl;  //prints 2

这是因为对引用的操作实际上是对引用所绑定的对象的操作。这意味着当我们 分配 给一个引用时,我们正在分配给引用所指向的对象 被绑定。当我们获取引用的值时,我们实际上是在获取引用绑定到的对象的值。


注意:
一旦初始化,引用仍然绑定到它的初始对象。无法重新绑定引用以引用不同的引用 对象。