参考变量的单向修改

One way modification of reference variables

本文关键字:修改 变量 参考      更新时间:2023-10-16

例如,我有这样的代码。

#include <iostream>
using namespace std;
int main()
{
    int x = 5;  //Original variable
    int &y = x; //Reference variable to x.
    y = 10;     //Modifying reference variable.
    cout<<x<<" "<<y<<endl;
    x = 5;      //Modifying original variable.
    cout<<x<<" "<<y<<endl;
}

它给出了预期的输出。

10 10
5 5

表明修改原始变量或引用变量都会改变它们——非常明显。


我的问题是:

引用变量是否可以定义为修改它不会修改原始变量

我知道它不会被称为引用变量

为了让事情更清楚,

修改x应修改y,但修改y不应修改x。也就是说,y应该是x的独立副本,但应该随着x的变化而变化。

这可能吗?


是的,我可以创建自己的逻辑来模仿这一点,但我想知道C++是否默认满足了这一点

没有一种语言结构可以立即为您提供所需的内容,但您可以通过封装x和y来实现它。

struct XChangesY{
   set_x(int x_and_y){  y = x = x_and_y; }
   set_y(int y_){ y = y_; }
   int get_x(){ return x; }
   int get_y(){ return y; }
private:
   int x;
   int y;
}

不,你想做的是不可能的。考虑一下如果同时修改yx会发生什么。那么y的取值在哪里?根据应同时修改xyx的更新值,还是根据应仅更新y而不更新xy的更新值?

唯一的前进道路是实现自己的逻辑。在任何情况下,都需要有两个int变量。

当然,但它并不优雅--

template<typename T>
class C
{
   public:
      C(T t) : m_t(new T(t)), t_local(t), is_copy(false) {}
      C(const C& c) : m_t(c.m_t), t_local(t), is_copy(true) {}
      T Get() { return *m_t; }
      T GetLocal() { return t_local; }
      void Set(T t) 
      { 
           t_local = t; 
           if (!is_copy) *m_t = t;
      }
   private:
      T* m_t;
      T t_local;
      bool is_copy;
};

当评估y时,您的设置会产生歧义,在两者都更改后——您想要x的值,还是y的新的不同值?我看不出有任何方法可以避免为每个案例创建单独的方法。

C++没有提供自动执行此操作的机制,但您可以创建一个支持您尝试创建的行为的类。

以下是一个过于简单化的版本:

class IntRef {
    int *ptr;
    int copy;
public:
    IntRef(int& d) : ptr(&d) {}
    IntRef& operator=(const int& rhs) {
        // Detach from the original on assignment
        copy = rhs;
        ptr = &copy;
    }
    operator int() const {
        return *ptr;
    }
};
int main() {
    int x = 5;   //Original variable
    IntRef y(x); //Reference variable to x.
    x = 5;      //Modifying original variable.
    cout<<x<<" "<<y<<endl;
    y = 10;      //Modifying reference variable.
    cout<<x<<" "<<y<<endl;
    return 0;
}

演示。

这将打印

5 5
5 10

上述实现的思想是,只要未分配y,就保持指向原始值的指针。赋值后,指针切换到内部保存的副本。

您所寻找的并不是内置的语言——两个变量共享相同的值,更改一个变量会同时更改两个变量,但更改另一个变量只会更改一个。

不过,这个问题类似于共享指针和引用计数器。例如,当您复制std::string时,不会复制字符串的内容。创建一个新的string对象,该对象具有指向与第一个字符串相同的数据的指针和引用计数器。

因此,两个字符串共享相同的数据。但是,当您更改一个(任意一个)并且引用计数器不止一个时,则string会自行取消关联并复制数据,并且两个字符串不再链接。这是为了在不需要时避免重复的长数据串。

同样的情况也发生在Qt容器(QByteArrayQMapQList…)上

另一种看待问题的方法是观察者逻辑——从外部接收的数据会更新内部值,但内部值可以通过其他方式更改。Qt的信号/插槽功能可以很自然地做到这一点,也可以很容易地实现自己的逻辑来实现这一点。

似乎您想要的是写时复制语义。像这样的东西可能对你有用,但很容易被滥用:

template <class T>
class cow_ptr
{
    public:
        using ref_ptr = std::shared_ptr<T>;
    private:
        ref_ptr m_sp;
        bool m_original; //don't detach the original
        void detach()
        {
            T* tmp = m_sp.get();
            if( !( tmp == 0 || m_sp.unique() || m_original ) ) {
                m_sp = ref_ptr( new T {*tmp} );
            }
        }
    public:
        cow_ptr(T* t)
            :   m_sp{t}, m_original{true}
        {}
        cow_ptr(const ref_ptr& refptr)
            :   m_sp{refptr}, m_original{true}
        {}
        cow_ptr(const cow_ptr& cowptr)
            :   m_sp{cowptr.m_sp}, m_original{false}
        {}
        cow_ptr& operator=(const cow_ptr& rhs)
        {
            m_sp = rhs.m_sp;
            return *this;
        }
        const T& operator*() const
        {
            return *m_sp;
        }
        T& operator*()
        {
            detach();
            return *m_sp;
        }
        const T* operator->() const
        {
            return m_sp.operator->();
        }
        T* operator->()
        {
            detach();
            return m_sp.operator->();
        }
};

然后你可以这样使用它:

int main()
{
    auto x = cow_ptr<int>{ new int{5} };
    auto y = x;
    const auto &yr = y; //so dereferencing doesn't detach
    cout<<*x<<" "<<*yr<<endl; //5 5
    *x = 10;
    cout<<*x<<" "<<*yr<<endl; //10 10 (updating x updated y)
    *y = 15;
    cout<<*x<<" "<<*yr<<endl; //10 15 (updating y did not update x)
    //from now on, changing x will not change y
}