4

どこかのライブラリ (変更するアクセス権がない) に、Counter クラスがあるとします。

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

これは、その性質上、変更可能です。const の場合は、かなり価値がありません。

コードでは、そのカウンターを「使用」します。ひどく。

#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() cCounter

(最低レベルでは、これを強制することはほとんど不可能であることは承知しています。私が望むのは、誤って実行しにくくする方法です。)

4

4 に答える 4

4

カウンター自体を変更せずにこれを確認できる最もクリーンなソリューションは、次のようなものです。

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

「oh noes」行から正しくエラーが発生します。(例では C++11 を使用していますが、C++98 のソリューションも同様です)

それは次のような使用法を除外しません:

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

もちろん、Counterそれ自体を変更しない限り、それは避けられないと思います。

于 2012-08-06T00:04:10.763 に答える
3

これを行う最も簡単な方法は、代入演算子を Counter クラスから削除することです。ただし、Counter クラスを変更する機能がないため、代入演算子のないクラスで Counter クラスをラップし、代わりにそれを使用するしかありません。

于 2012-08-05T23:58:47.907 に答える
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;
}

オブジェクトに対して実行できる操作を制限したい場合はいつでも、このメソッドを使用できます。

編集:おそらくすでにこれを認識しており、より洗練されたソリューションを探していますが、コードは他の人に役立つかもしれません.

于 2012-08-06T00:51:12.473 に答える
1

変更可能な参照を介して許可することによりbump、関数にオブジェクトの状態を混乱させるアクセス権を与えています。代入について特別なことは何もありません。何らかの方法でオブジェクトを変更する関数です。CopyStateFromAnotherInstance()の代わりに呼び出される関数でもかまいませんoperator =()

したがって、本当の問題は次のとおりです。特定の機能のみを許可し、他の機能を非表示にするにはどうすればよいでしょうか。インターフェイスを使用して:

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

class Counter : IBumpable
{
    ....
};

void functionThatCannotBreakCounter(IBumpable& counter) { ... }
于 2012-08-06T00:04:00.907 に答える