これがスタイルの質問なのか、それとも難しいルールがあるのかわからない...
パブリックメソッドインターフェイスを可能な限り一定に保ちながら、オブジェクトスレッドを安全にしたい場合は、可変ミューテックスを使用する必要がありますか?一般に、これは優れたスタイルですか、それとも非constメソッドインターフェイスを優先する必要がありますか?あなたの見解を正当化してください。
隠された質問は、クラスを保護するミューテックスをどこに置くかということです。
要約すると、ミューテックスによって保護されているオブジェクトのコンテンツを読み取りたいとします。
「read」メソッドは、オブジェクト自体を変更しないため、意味的に「const」である必要があります。ただし、値を読み取るには、ミューテックスをロックし、値を抽出してから、ミューテックスのロックを解除する必要があります。つまり、ミューテックス自体を変更する必要があります。つまり、ミューテックス自体を「const」にすることはできません。
その後、すべてが大丈夫です。オブジェクトは「const」にすることができ、ミューテックスは次のようにする必要はありません。
Mutex mutex ;
int foo(const Object & object)
{
Lock<Mutex> lock(mutex) ;
return object.read() ;
}
私見、これは悪い解決策です。なぜなら、誰もがミューテックスを再利用して他のものを保護することができるからです。あなたを含みます。実際、コードが十分に複雑な場合、これまたはそのミューテックスが正確に何を保護しているかについて混乱するだけなので、自分を裏切ることになります。
私は知っています:私はその問題の犠牲者でした。
カプセル化の目的で、ミューテックスを保護しているオブジェクトからできるだけ近くに配置する必要があります。
通常、内部にミューテックスを含むクラスを作成します。しかし遅かれ早かれ、複雑なSTL構造、または内部にミューテックスなしで別の人が書いたものを保護する必要があります(これは良いことです)。
これを行う良い方法は、ミューテックス機能を追加する継承テンプレートを使用して元のオブジェクトを派生させることです。
template <typename T>
class Mutexed : public T
{
public :
Mutexed() : T() {}
// etc.
void lock() { this->m_mutex.lock() ; }
void unlock() { this->m_mutex.unlock() ; } ;
private :
Mutex m_mutex ;
}
このように、あなたは書くことができます:
int foo(const Mutexed<Object> & object)
{
Lock<Mutexed<Object> > lock(object) ;
return object.read() ;
}
問題は、object
がconstであり、lockオブジェクトがnon-constメソッドlock
とunlock
メソッドを呼び出しているために機能しないことです。
ビット単位のconstオブジェクトに限定されていると思われる場合はconst
、問題が発生しているため、「外部ミューテックスソリューション」に戻る必要があります。
解決策は、(クラスのメソッド修飾子として使用される場合のconst
ように)よりセマンティック修飾子であることを認めることです。volatile
クラスが完全ではないという事実を隠していますがconst
、メソッドを呼び出すときにクラスの意味のある部分が変更されないという約束を守る実装を提供するようにしてconst
ください。
次に、ミューテックスミュータブルとロック/ロック解除メソッドを宣言する必要がありますconst
。
template <typename T>
class Mutexed : public T
{
public :
Mutexed() : T() {}
// etc.
void lock() const { this->m_mutex.lock() ; }
void unlock() const { this->m_mutex.unlock() ; } ;
private :
mutable Mutex m_mutex ;
}
内部ミューテックスソリューションは良いものです。私見:一方の手でオブジェクトをもう一方の近くで宣言し、もう一方の手で両方をラッパーに集約することは、最終的には同じことです。
ただし、集計には次の長所があります。
したがって、mutexをmutexedオブジェクトにできるだけ近づけて(たとえば、上記のMutexed構造を使用して)、mutable
mutexの修飾子を探します。
どうやら、ハーブサッターは同じ視点を持っています: C ++11の「新しい」意味についての彼のプレゼンテーションconst
は非常に啓発的です:mutable
http://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/
[回答編集]
基本的に、可変ミューテックスでconstメソッドを使用することは、少なくともオブジェクトを変更しないことを示すために、良い考えです(ちなみに、参照を返さないでください。必ず値で返すようにしてください)。ミューテックスはconstであってはなりません。lock/unlockメソッドをconstとして定義するのは恥知らずな嘘です...
実際、これ(およびメモ化)は、私がmutable
キーワードを使用する唯一の公正な使用法です。
オブジェクトの外部にあるミューテックスを使用することもできます。すべてのメソッドが再入可能になるように調整し、ユーザーが自分でロックを管理できるようにします。入力{ lock locker(the_mutex); obj.foo(); }
はそれほど難しくありません。
{
lock locker(the_mutex);
obj.foo();
obj.bar(42);
...
}
2つのミューテックスロックを必要としないという利点があります(オブジェクトの状態が変更されなかったことが保証されます)。