1

オブジェクトへの書き込み専用参照をコーディングする方法はありますか? たとえば、次のミューテックス クラスがあるとします。

template <class T> class mutex {
protected:
   T _data;
public:
   mutex();
   void lock(); //locks the mutex
   void unlock(); //unlocks the mutex
   T& data(); //returns a reference to the data, or throws an exception if lock is unowned
};

これができないことを保証する方法はありますか:

mutex<type> foo;
type& ref;
foo.lock();
foo.data().do_stuff();
ref = foo.data();
foo.unlock();
//I have a unguarded reference to foo now

一方で、それだけの価値はありますか?プログラマーが故意にシステムを破壊することはないと考える人がいることは知っていますが、そもそもなぜプライベート変数があるのでしょうか? 「未定義の動作」とだけ言っておけばよかったのですが、それは少し安全ではないように思えます。

編集: OK、セッター ルーチンのアイデアは理解できますが、これはどのように実現できますか?

mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   foo.data().push_back(i);
}

foo.unlock(); set ルーチンを使用すると、書き込みごとにコピーが必要になります。

mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   vector<int> copy = foo.read();
   copy.push_back(i);
   foo.write(copy);
}

この特定のケースでは自明に最適化できますが、たとえば、いくつかの異なるスレッドがすべて要素をプッシュしており、いくつかのスレッドを消去している場合、これはかなりの過剰なメモリ コピー (つまり、クリティカル セクションごとに 1 つ) になる可能性があります。

4

5 に答える 5

2

はい、ロック解除が呼び出されたときに無効になるラッパークラスを作成して、参照を返す代わりにラッパーを返すことができます。また、その代入演算子をオーバーロードして、参照に割り当てることができます。秘訣は、ラッパーの内部データへの参照を保持する必要があることです。これにより、ロックを解除する前に、ロックを解除するときに、作成したラッパーを無効にすることができます。

于 2010-04-23T00:09:48.057 に答える
2

ゲッターとセッターを区別する一般的な方法は、オブジェクトの const-ness によるものです。

template <class T> class mutex {
public:
   mutex();
   void lock();
   void unlock();
         T& data();       // cannot be invoked for const objects
   const T& data() const; // can be invoked for const objects
protected:
   T _data;
};

ここで、読み取り専用アクセスが必要な場合は、ミューテックスを const にします。

void read_data(const mutex< std::vector<int> >& data)
{
   // only const member functions can be called here
}

非 const オブジェクトを const 参照にバインドできます。

// ...
mutex< std::vector<int> > data;
data.lock();
read_data(data);
data.unlock();
// ...

lock()およびunlock()関数は、例外に直面すると本質的に安全ではないことに注意してください。

void f(const mutex< std::vector<int> >& data)
{
  data.lock();
  data.data().push_back(42); // might throw exception
  data.unlock(); // will never be reached in push_back() throws
}

これを解決する通常の方法はRAIIです (リソースの取得は初期化です):

template <class T> class lock;

template <class T> class mutex {
public:
   mutex();
protected:
   T _data;
private:
   friend class lock<T>;
   T& data();
   void lock();
   void unlock();
};

template <class T> class lock {
public:
  template <class T> {
  lock(mutex<T>& m) m_(m) {m_.lock();}
  ~lock()                 {m_.unlock();}

         T& data()        {return m_.data();}
   const T& data() const  {return m_.data()}
private:
  mutex<T>& m_;
};

ロックされていないデータにアクセスする方法がないように、アクセサー関数もロック クラスに移動したことに注意してください。

これは次のように使用できます。

void f(const mutex< std::vector<int> >& data)
{
  {
    lock< std::vector<int> > lock_1(data);
    std::cout << lock1.data()[0]; // fine, too
    lock1.data().push_back(42);   // fine
  }
  {
    const lock< std::vector<int> > lock_2(data); // note the const
    std::cout << lock1.data()[0];  // fine, too
    // lock1.data().push_back(42); // compiler error
  }
}
于 2010-04-23T05:48:46.773 に答える
1

データを非公開としてカプセル化し、書き込みルーチンを公開できます。そのルーチン内で、ミューテックスをロックして、狙っているものと同様の動作をさせることができます。

于 2010-04-22T23:55:51.810 に答える
0

メンバー関数は次のように使用できます。

void set_data(const T& var);

これは、C++ で書き込み専用アクセスが適用される方法です。

于 2010-04-22T23:53:34.123 に答える
-2

いいえ。すべてのメモリが 1 つの大きな配列のように扱われる C++ のような安全でない言語でのメモリの読み取りと書き込みについて、保証する方法はありません。


[編集]なぜすべての反対票が投じられたのかわかりません。これは正しく、適切です。

Java や C# などの安全な言語では、たとえば、適切に実装された不変型が不変のままであることを確実に保証できます。そのような保証は C++ では決してできません。

恐れているのは悪意のあるユーザーではなく、偶発的な無効ポインターです。私は、完全に無関係なコードの無効なポインターが原因で不変型が変更され、追跡が非常に困難なバグを引き起こしている C++ プロジェクトに取り組んできました。安全な言語だけができるこの保証は、便利で重要です。

于 2010-04-22T23:54:18.810 に答える