C++ の書き込み専用リファレンス?
質問
オブジェクトへの書き込み専用参照をコーディングする方法はありますか?たとえば、mutex クラスがあったとします。
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 つ)。
正しい解決策はありません
他のヒント
はい、ロック解除が呼び出されると無効になるラッパークラスを作成し、参照を返す代わりにラッパーを返すことができ、割り当てオペレーターをオーバーロードしてリファレンスに割り当てることができます。トリックは、ラッパーの内部データへの参照に固執する必要があることです。そのため、ロックを解除する前にロックを解除すると、作成したラッパーを無効にすることです。
ゲッターとセッターを区別する一般的な方法は、オブジェクトの一時性です。
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;
};
ここで、読み取り専用アクセスを必要とする場合は、Mutex constを作成します。
void read_data(const mutex< std::vector<int> >& data)
{
// only const member functions can be called here
}
Const Referenceに非コンストオブジェクトをバインドできます。
// ...
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
}
}
データをプライベートとしてカプセル化し、書き込みルーチンを公開することができます。そのルーチンの中で、あなたはあなたのミューテックスをロックすることができ、あなたが撮影しているものと同様の行動を与えることができます。
メンバー関数を次のように使用できます。
void set_data(const T& var);
これは、書き込み専用アクセスがC ++で適用される方法です。
いいえ。C++ のような安全でない言語では、すべてのメモリが 1 つの大きな配列のように扱われるため、メモリの読み書きについて何も保証する方法がありません。
[編集] なぜ反対票が集まったのかわかりません。これは正しく、適切です。
Java や C# などの安全な言語では、たとえば、適切に実装された不変型が不変のままであることを確実に保証できます。C++ ではそのような保証は決してできません。
懸念されるのは悪意のあるユーザーではなく、偶発的な無効ポインタです。私が取り組んだ C++ プロジェクトでは、まったく関係のないコード内の無効なポインターが原因で不変型が変更され、次のようなバグが発生しました。 非常に 追跡するのが難しい。この保証は、安全な言語だけが行うことができるものであり、有用であると同時に重要です。