防止重新分配参考?

时间:2012-08-05 23:50:41

标签: c++ pass-by-reference

考虑到某个某个库(我们无权更改),我们有一个Counter类:

class Counter {
    int count;
  public:
    Counter() : count(0) { }
    void bump() { ++count; }
    int getCount() const { return count; }
};

,就其本质而言,是可变的。如果它是常量,它就毫无价值。

在我们的代码中,我们“使用”那个Counter。不好。

#include <string>
#include <iostream>
#include <Counter.hpp>

using std::cout;
using std::endl;

void breakTheHellOutOfCounter(Counter &c) {
    // This is OK
    c.bump();

    // Oh noes!
    c = Counter();
}

int main() {
    Counter c;
    c.bump(); c.bump(); c.bump();
    std::cout << "Count was " << c.getCount() << std::endl;
    breakTheHellOutOfCounter(c);
    std::cout << "Count is now " << c.getCount() << std::endl;
}

请注意breakTheHellOutOfCounter用闪亮的新计数器覆盖main的计数器,重置计数。这会让来电者感到悲伤。 (想象一下更有害的事情,你会看到我要去的地方。)

我需要能够碰撞c(因此,我需要它可变),但我希望breakTheHellOutOfCounter()由于尝试替换 {{而失败1}}。有没有办法可以改变事物(c类除外)以实现这一目标?

(我知道在最低级别,这几乎是不可能强制实施的。我想要的是一种让你很难意外做的方法。)

4 个答案:

答案 0 :(得分:3)

最简单的方法是从Counter类中删除赋值运算符。但是,由于您无法更改Counter类,因此您唯一真正的选择是将Counter类包装在没有赋值运算符的类中,而是使用它。

答案 1 :(得分:3)

我可以在不修改计数器的情况下看到的最干净的解决方案就像:

#include <string>
#include <iostream>
#include <Counter.hpp>

template <typename T>
struct Unbreakable : public T {
  Unbreakable<T>& operator=(const Unbreakable<T>&) = delete;
  Unbreakable<T>& operator=(Unbreakable<T>&&) = delete;

  template <typename ...Args>
  Unbreakable(Args&& ...args) : T(std::forward<Args>(args)...) {}
};

using std::cout;
using std::endl;

void breakTheHellOutOfCounter(Unbreakable<Counter> &c) {
    // this is ok
    c.bump();

    // oh noes!
    c = Counter();
}


int main() {
    Unbreakable<Counter> c;
    c.bump(); c.bump(); c.bump();
    std::cout << "Count was " << c.getCount() << std::endl;
    breakTheHellOutOfCounter(c);
    std::cout << "Count is now " << c.getCount() << std::endl;
}

哪个正确地给出了“哦不”线的错误。 (示例使用C ++ 11,但C ++ 98解决方案类似)

这并不排除使用方式:

Counter& br = c;
br = Counter();

当然,但如果不修改Counter本身,我认为这是不可避免的。

答案 2 :(得分:2)

正如Michael Anderson所说,你可以将你的计数器对象包装在一个阻止赋值的类中。

class CounterProxy {
    Counter& counter;
    CounterProxy & operator=(const CounterProxy&);
public:
    CounterProxy(Counter& c) : counter(c) {}
    void bump() { counter.bump(); }
    int getCount() const { return counter.getCount(); }
};

void breakTheHellOutOfCounter(CounterProxy &c) {
    // this is ok
    c.bump();

    // not oh noes!
    c = CounterProxy(Counter());
}

int main() {
    Counter c;
    c.bump(); c.bump(); c.bump();
    std::cout << "Count was " << c.getCount() << std::endl;
    breakTheHellOutOfCounter(CounterProxy(c));
    std::cout << "Count is now " << c.getCount() << std::endl;
}

只要您想限制可以对对象执行的操作,就可以使用此方法。

编辑:您可能已经意识到这一点并寻找更优雅的解决方案,但代码可能会帮助其他人。

答案 3 :(得分:1)

通过允许bump通过可变引用,您可以让函数访问对象状态。任务没什么特别之处;它只是一个以某种方式改变对象的函数。它也可以是名为CopyStateFromAnotherInstance()的函数,而不是operator =()

所以真正的问题是:你如何仅允许某些功能而是隐藏其他功能?通过使用界面:

class IBumpable
{
    void bump() ...
};

class Counter : IBumpable
{
    ....
};

void functionThatCannotBreakCounter(IBumpable& counter) { ... }