考虑到某个某个库(我们无权更改),我们有一个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
类除外)以实现这一目标?
(我知道在最低级别,这几乎是不可能强制实施的。我想要的是一种让你很难意外做的方法。)
答案 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) { ... }