修改参考变量的一种方法

时间:2015-03-24 12:01:15

标签: c++

例如,我有这段代码。

#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 ++默认是否适应这种情况。

6 个答案:

答案 0 :(得分:2)

没有语言结构能够立即为您提供所需的内容,但您可以通过封装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;
}

答案 1 :(得分:1)

不,你想做什么是不可能的。考虑如果同时修改yx会发生什么。那么y取其价值在哪里?来自x的更新值,该值应修改xy,或更新y的值,该值应仅更新y而不是{{} 1}}?

唯一的出路是实现自己的逻辑。无论如何,您需要有两个x变量。

答案 2 :(得分:1)

当然,但它并不优雅 -

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的值,还是{{1}的新的不同值}}?我没有办法避免为每种情况创建单独的方法。

答案 3 :(得分:1)

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

Demo.

打印

5 5
5 10

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

答案 4 :(得分:1)

您正在寻找的内容并非固有地内置于该语言中 - 两个变量共享相同的值,更改一个更改两个但更改另一个只更改一个。

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

因此两个字符串共享相同的数据。但是当你改变一个 - 任何一个 - 并且引用计数器不止一个时,那个string会自我分离并复制数据,并且两个字符串都不再链接。这是为了避免在没有必要时重复长串数据。

Qt容器(QByteArrayQMapQList,...)也会发生同样的事情。

另一种查看问题的方法是观察者的逻辑 - 从外部接收的数据更新内部值,但内部值可以通过其他方式更改。 Qt的信号/插槽功能可以自然地用来做,或者你可以轻松实现自己的逻辑来自己做。

答案 5 :(得分:1)

似乎你想要的是 copy-on-write 语义。这样的事情可能适合你,但很容易被滥用:

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
}