少し前に、クラスのメンバー変数をmutable
キーワードでマークするコードに出くわしました。私が見ることができる限り、それは単にあなたがconst
メソッドで変数を修正することを可能にします:
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
これはこのキーワードの唯一の使用法ですか?それとも目に見える以上のものがありますか?私はそれ以来boost::mutex
をミュータブルとしてマークし、スレッドセーフの理由でconst
関数がそれをロックすることができるようにクラスでこのテクニックを使いました、しかし、正直に言うと、それはちょっとしたハックのように感じます。
それはビットごとのconstと論理的constの区別を可能にします。論理定数とは、ロックの例のように、オブジェクトがパブリックインターフェイスを介して表示されるように変化しない場合です。もう1つの例は、値が最初に要求されたときに値を計算し、その結果をキャッシュするクラスです。
C++ 11のmutable
は、値によってキャプチャされたものが変更可能であることを示すためにラムダに使用できるため(デフォルトではありません):
int x = 0;
auto f1 = [=]() mutable {x = 42;}; // OK
auto f2 = [=]() {x = 42;}; // Error: a by-value capture cannot be modified in a non-mutable lambda
mutable
キーワードは、オブジェクトを覆うconst
ベールに穴を開ける方法です。オブジェクトへのconst参照またはポインターがある場合は、mutable
とマークされているとき、どのようにマークされているかによって、そのオブジェクトを変更することはできませんexcept 。
const
参照またはポインターを使用すると、次の制約があります。
const
としてマークされているメソッドのみを呼び出す許可。mutable
例外により、mutable
とマークされたデータメンバーを記述または設定できるようになります。それが外部から見える唯一の違いです。
内部的に、あなたに見えるconst
メソッドは、mutable
とマークされたデータメンバーに書き込むこともできます。基本的に、const veilは包括的に貫通されます。 mutable
がconst
の概念を破壊せず、有用な特別な場合にのみ使用されるようにすることは、APIデザイナー次第です。 mutable
キーワードは、これらの特殊なケースの対象となるデータメンバーを明確にマークするので役立ちます。
実際には、コードベース全体でconst
を強引に使用できます(基本的にconst
"disease"でコードベースを "感染"させたい)。この世界では、ポインターと参照はconst
であり、例外はほとんどなく、推論と理解が容易なコードを生成します。興味深い余談については、「参照の透明性」を参照してください。
mutable
キーワードがないと、最終的にconst_cast
を使用して、許可されるさまざまな便利な特殊なケース(キャッシュ、参照カウント、デバッグデータなど)を処理する必要があります。残念ながら、const_cast
はmutable
よりもかなり破壊的です。APIclientがconst
保護を破壊するためです。彼が使用しているオブジェクト。さらに、広範囲のconst
破壊を引き起こします。constポインターまたは参照をconst_cast
ingすることで、制限のない書き込みおよびメソッド呼び出しで可視メンバーにアクセスできます。対照的に、mutable
では、API設計者がconst
例外をきめ細かく制御する必要があり、通常、これらの例外はconst
メソッドで非公開データで動作します。
(NBデータとメソッドvisibilityを数回参照します。パブリックまたはプライベートまたは保護としてマークされたメンバーについて話します。まったく異なるタイプのオブジェクト保護について説明します。 ここ 。)
Boost :: mutexを使用することはまさにこのキーワードが意図されていることです。もう1つの用途は、アクセスを高速化するための内部結果キャッシュです。
基本的に、 'mutable'はオブジェクトの外部から見える状態に影響を与えないあらゆるクラス属性に適用されます。
問題のサンプルコードでは、done_の値が外部状態に影響を与える場合、mutableは不適切かもしれません。部。
Mutableは特定の属性をconst
メソッド内から変更可能としてマークするためのものです。それが唯一の目的です。 mutable
を使用するのではなくデザインを変更すると、コードがおそらくよりクリーンで読みやすくなるため、使用する前に慎重に検討してください。
http://www.highprogrammer.com/alan/rants/mutable.html
それで、上記の狂気がミュータブルのためではないのなら、それは何のためのものですか?これは微妙なケースです:mutableはオブジェクトが論理的に一定であるケースのためですが、実際には変更する必要があります。これらのケースはごくわずかで、その間にありますが、存在します。
作者が与える例としては、キャッシングや一時的なデバッグ変数があります。
キャッシュのように内部の状態を隠しているような状況で役立ちます。例えば:
class HashTable { ... [ public: ]文字列参照(文字列キー)const { ] if(key == lastKey) lastValueを返す; 文字列値= lookupInternal(key); lastKey = key; [。 lastValue = value; 戻り値; } private: 可変文字列lastKey、lastValue; };
それから、const HashTable
オブジェクトにそのlookup()
メソッドを使用させ、内部キャッシュを変更することができます。
mutable
は、そうでなければ定数関数内のデータを変更できるようにするとあなたが推測したように存在します。
その意図は、あなたがオブジェクトの内部状態に「何もしない」という関数を持っているかもしれないので、あなたは関数const
をマークするが、本当にそのオブジェクトの状態に影響を与えない方法で変更する必要があるかもしれません正しい機能.
キーワードは、コンパイラに対するヒントとして機能することがあります。理論上のコンパイラは、読み取り専用とマークされた定数オブジェクト(グローバルなど)をメモリに配置できます。 mutable
の存在は、これが行われるべきではないことを示唆しています。
可変データを宣言して使用する正当な理由は次のとおりです。
mutable boost::mutex
を宣言することは完全に合理的です。ええ、それはそれがすることです。論理的にクラスの状態を変更しないメソッドによって変更されたメンバには、これを使用します。たとえば、キャッシュを実装してルックアップを高速化するためです。
class CIniWrapper
{
public:
CIniWrapper(LPCTSTR szIniFile);
// non-const: logically modifies the state of the object
void SetValue(LPCTSTR szName, LPCTSTR szValue);
// const: does not logically change the object
LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;
// ...
private:
// cache, avoids going to disk when a named value is retrieved multiple times
// does not logically change the public interface, so declared mutable
// so that it can be used by the const GetValue() method
mutable std::map<string, string> m_mapNameToValue;
};
const
メソッドのみを使用する場合、呼び出し側はそれらがスレッドセーフであると見なす可能性があるため、並行性の問題は大きな問題です。そしてもちろん、mutable
データを変更してもオブジェクトの動作が大きく変わることはありません。たとえば、ディスクに書き込まれた変更がすぐに表示されることが予想される場合は、私が示した例に違反します。アプリ。
Mututeは、クラス内に変数があり、そのクラス内でのみ使用される変数がある場合に使用されます(ミューテックスやロックなど)。この変数はクラスの動作を変更しませんが、クラス自体のスレッドセーフを実装するために必要です。したがって、 "mutable"がなければ、 "const"関数を持つことはできません。なぜなら、この変数は、外界で利用可能なすべての関数で変更する必要があるからです。そのため、メンバー変数をconst関数でも書き込み可能にするためにmutableが導入されました。
指定されたmutableは、安全で、メンバ変数がconstメンバ関数内で変更されることが予想されることをコンパイラと読者の両方に知らせます。
C++の多くのものと同じように、変更可能なは、行きたくない怠惰なプログラマーにとってはハックになり得ますずっとさかのぼって、constであってはいけないものをnon-constとしてマークします。
mutableは主にクラスの実装の詳細に使われます。クラスのユーザはそれについて知る必要はありません。そのため、彼は「あるべきだ」と考えるメソッドを「する」ことができます。ミューテックスをミュータブルにするあなたの例は、良い標準的な例です。
ユーザーに対して論理的にステートレスであり(したがってパブリッククラスのAPIには「const」ゲッターがあるはずですが)、基礎となるIMPLEMENTATION(.cpp内のコード)ではステートレスではない場合は、「可変」を使用します。
私が最も頻繁に使用するケースは、ステートレスな "plain old data"メンバーの遅延初期化です。つまり、狭いケースでは、そのようなメンバが構築(プロセッサ)または持ち運び(メモリ)するのに費用がかかり、オブジェクトの多くのユーザが決して要求しないことが理想的です。構築されたオブジェクトの90%がまったく構築する必要がないため、パフォーマンスを向上させるためにバックエンドで遅延構築を行う必要がありますが、それでもパブリック消費用に正しいステートレスAPIを提示する必要があります。
ミュータブルを使用する最良の例の1つは、ディープコピーです。コピーコンストラクタでは、引数としてconst &obj
を送ります。そのため、作成された新しいオブジェクトは定数型になります。この新しく作成されたconstオブジェクトのメンバを変更したい場合(ほとんど変更することはほとんどありませんが、変更することがあります)、mutable
として宣言する必要があります。
mutable
記憶クラスはクラスの非静的非constデータメンバでのみ使用できます。クラスの可変データメンバは、constとして宣言されているオブジェクトの一部であっても変更できます。
class Test
{
public:
Test(): x(1), y(1) {};
mutable int x;
int y;
};
int main()
{
const Test object;
object.x = 123;
//object.y = 123;
/*
* The above line if uncommented, will create compilation error.
*/
cout<< "X:"<< object.x << ", Y:" << object.y;
return 0;
}
Output:-
X:123, Y:1
上記の例では、メンバ変数x
の値はconstとして宣言されているオブジェクトの一部ですが、変更することができます。これは、変数x
が可変として宣言されているためです。しかし、メンバ変数y
の値を変更しようとすると、コンパイラはエラーをスローします。
Mutableキーワードは、クラステストの目的でスタブを作成するときに非常に便利です。あなたはconst関数をスタブすることができます、そしてあなたがまだあなたのスタブに追加した(変わりやすい)カウンターまたはどんなテスト機能でも増やすことができます。これにより、スタブ化されたクラスのインタフェースはそのまま残ります。
場合によっては(設計が不適切なイテレータのように)、クラスはカウントまたはその他の付随的な値を保持する必要がありますが、それはクラスの主要な「状態」には実際には影響しません。これはほとんどの場合、私がミュータブルを使っているところです。変更可能でなければ、あなたはあなたのデザインの全体的な制約を犠牲にすることを余儀なくされるでしょう。
私にとっても、それはハックのような気がします。ごくわずかな状況で役立ちます。
(他の答えで述べたように)古典的な例と私がこれまでに使ったmutable
キーワードが見た唯一の状況は、キャッシュがクラスのデータメンバーとして実装されているのではなく、複雑なGet
メソッドの結果をキャッシュするためです。メソッド内の静的変数として(いくつかの関数間で共有するため、またはわかりやすさのために).
一般に、mutable
キーワードを使用する代わりに、通常はメソッド内の静的変数またはconst_cast
トリックを使用します。
もう一つの詳しい説明はここ にあります 。
Mutableはクラスのconst
の意味をbitwise constからlogical constに変更します。
つまり、可変メンバーを持つクラスはビット単位のconstになり、実行可能ファイルの読み取り専用セクションには表示されなくなります。
さらに、const
メンバー関数がconst_cast
を使用せずに可変メンバーを変更できるようにして、型チェックを変更します。
class Logical {
mutable int var;
public:
Logical(): var(0) {}
void set(int x) const { var = x; }
};
class Bitwise {
int var;
public:
Bitwise(): var(0) {}
void set(int x) const {
const_cast<Bitwise*>(this)->var = x;
}
};
const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.
int main(void)
{
logical.set(5); // Well defined.
bitwise.set(5); // Undefined.
}
詳細については他の回答を参照してください。しかし、これは単に型安全のためではなく、コンパイル結果に影響を与えることを強調したいと思いました。
あなたがconst仮想関数をオーバーライドしていて、その関数の中であなたの子クラスメンバ変数を修正したいとき、mutableは役に立ちます。ほとんどの場合、基底クラスのインターフェースを変更したくないので、自分の可変メンバー変数を使用する必要があります。
まさしくそのキーワード 'mutable'は、実際には予約されたキーワードです。定数variableの値を変えるのに使われることが多いです。
//Prototype
class tag_name{
:
:
mutable var_name;
:
:
};