575

mutable少し前に、クラスのメンバー変数をキーワードでマークするコードに出くわしました。私が見る限り、constメソッド内の変数を変更するだけです。

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

これがこのキーワードの唯一の用途ですか、それとも見た目以上のものがありますか? それ以来、クラスでこの手法を使用し、boost::mutexをミュータブルとしてマークして、const関数がスレッド セーフの理由でそれをロックできるようにしましたが、正直なところ、ちょっとしたハックのように感じます。

4

18 に答える 18

393

これにより、ビット単位の定数と論理定数を区別できます。論理定数とは、ロックの例のように、パブリックインターフェイスから見える方法でオブジェクトが変更されない場合です。もう1つの例は、最初に要求されたときに値を計算し、結果をキャッシュするクラスです。

c ++ 11mutableをラムダで使用して、値によってキャプチャされたものが変更可能であることを示すことができるため(デフォルトでは変更されません):

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
于 2008-09-19T20:04:14.100 に答える
155

mutableキーワードは、オブジェクトの上にドレープするベールを突き刺す方法ですconst。オブジェクトへの const 参照またはポインターがある場合、いつ、どのようにマークされているかを除いて、そのオブジェクトを変更することはできませんmutable

const参照またはポインターを使用すると、次のことが制限されます。

  • 可視データ メンバーの読み取りアクセスのみ
  • としてマークされたメソッドのみを呼び出す権限const

例外により、mutableマークされたデータ メンバーを書き込みまたは設定できるようになりましたmutable。それは、外から見える唯一の違いです。

内部const的には、可視のメソッドは、 とマークされたデータ メンバーにも書き込むことができますmutable。本質的にコンストベールは包括的に貫かれています。mutableが概念を破壊せずconst、有用な特別な場合にのみ使用されることを保証するのは、完全に API 設計者次第です。キーワードは、mutableこれらの特殊なケースの対象となるデータ メンバーを明確にマークするので役立ちます。

実際にはconst、コードベース全体で執拗に使用できます (基本的に、コードベースにconst「病気」を「感染させたい」)。この世界では、ポインターと参照にはconstほとんど例外がなく、推論と理解が容易なコードが生成されます。興味深い余談については、「参照透過性」を調べてください。

mutableキーワードがないと、最終的にconst_castは、それが許すさまざまな便利な特殊なケース (キャッシング、参照カウント、デバッグ データなど) を処理するために使用せざるを得なくなります。残念ながら、使用しているオブジェクトの保護を APIクライアントに強制的に破棄させるconst_castよりも、はるかに破壊的です。さらに、広範な破壊を引き起こします。 const ポインターまたは参照を使用すると、可視メンバーへの自由な書き込みおよびメソッド呼び出しアクセスが可能になります。対照的に、API 設計者は例外をきめ細かく制御する必要があり、通常、これらの例外はプライベート データを操作するメソッドに隠されています。mutableconstconstconst_castmutableconstconst

(注意: データとメソッドの可視性について何度か言及しています。ここで説明するオブジェクト保護のまったく異なるタイプである public と private または protected としてマークされたメンバーについて話しているのです。)

于 2010-03-05T02:34:00.533 に答える
78

boost::mutex での使用は、まさにこのキーワードが意図したものです。もう 1 つの用途は、アクセスを高速化するための内部結果キャッシュです。

基本的に、「可変」は、オブジェクトの外部から見える状態に影響を与えないクラス属性に適用されます。

あなたの質問のサンプル コードでは、done_ の値が外部状態に影響を与える場合、mutable は不適切である可能性があります。部。

于 2008-09-19T20:01:20.223 に答える
37

Mutable は、特定の属性をconstメソッド内から変更可能としてマークするためのものです。それが唯一の目的です。使用する前に慎重に検討してください。使用するのではなく設計を変更した方が、コードがきれいになり、読みやすくなるからですmutable

http://www.highprogrammer.com/alan/rants/mutable.html

上記の狂気がミュータブルの目的ではない場合、それは何のためにあるのでしょうか? ここに微妙なケースがあります: mutable は、オブジェクトが論理的に一定であるが、実際には変更する必要がある場合です。これらのケースはほとんどありませんが、存在します。

著者が提供する例には、キャッシングと一時的なデバッグ変数が含まれます。

于 2008-09-19T20:02:55.947 に答える
36

キャッシュなどの内部状態を非表示にしている場合に便利です。例えば:

クラスHashTable
{{
..。
公衆:
    文字列ルックアップ(文字列キー)const
    {{
        if(key == lastKey)
            lastValueを返します。

        文字列値=lookupInternal(key);

        lastKey=キー;
        lastValue=値;

        戻り値;
    }

プライベート:
    可変文字列lastKey、lastValue;
};

そして、const HashTableオブジェクトにそのlookup()メソッドを使用させることができます。これにより、内部キャッシュが変更されます。

于 2008-09-19T20:03:13.790 に答える
10

mutableそれ以外の場合は一定の関数でデータを変更できるようにすることを推測すると、存在しません。

その意図は、オブジェクトの内部状態に対して「何もしない」関数を持っている可能性があるため、関数をマークすることconstですが、正しい状態に影響を与えない方法でオブジェクトの状態の一部を実際に変更する必要がある場合があります。機能。

キーワードは、コンパイラへのヒントとして機能する場合があります。理論上のコンパイラは、読み取り専用とマークされた定数オブジェクト (グローバルなど) をメモリに配置できます。mutableこれを行うべきではないというヒントの存在。

可変データを宣言して使用する正当な理由を次に示します。

  • スレッドセーフ。a を宣言することmutable boost::mutexは完全に合理的です。
  • 統計学。一部またはすべての引数を指定して、関数の呼び出し回数をカウントします。
  • メモ化。高価な答えを計算し、再計算するのではなく、将来の参照用に保存します。
于 2008-09-19T20:25:03.350 に答える
8

ええ、ええ、それはそれがすることです。クラスの状態を論理的に変更しないメソッドによって変更されるメンバーに使用します。たとえば、キャッシュを実装することでルックアップを高速化します。

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データを変更しても、オブジェクトの動作が大幅に変更されることはありません。たとえば、ディスクに書き込まれた変更がアプリにすぐに表示されることが予想される場合、私が示した例では違反する可能性があります。 。

于 2008-09-19T20:07:14.280 に答える
7

ユーザーにとって論理的にステートレスである (したがって、パブリック クラスの API に "const" ゲッターが必要である) が、基になる IMPLEMENTATION (.cpp のコード) でステートレスでない場合は、"mutable" を使用します。

私が最も頻繁に使用するケースは、状態のない「プレーンな古いデータ」メンバーの遅延初期化です。つまり、そのようなメンバーの構築 (プロセッサ) または持ち運び (メモリ) にコストがかかり、オブジェクトの多くのユーザーがそれらを決して要求しないという狭いケースで理想的です。そのような状況では、構築されたオブジェクトの 90% はまったく構築する必要がないため、パフォーマンスのためにバックエンドで遅延構築を行う必要がありますが、パブリック消費のために正しいステートレス API を提示する必要があります。

于 2014-01-14T02:38:37.357 に答える
7

Mutable は、そのクラス内でのみ使用される変数がクラス内にある場合に使用され、ミューテックスやロックなどのシグナルを送信します。この変数はクラスの動作を変更しませんが、クラス自体のスレッド セーフを実装するために必要です。したがって、「mutable」がなければ、「const」関数を使用することはできません。これは、この変数を外部で使用できるすべての関数で変更する必要があるためです。そこで、メンバー変数を const 関数でも書き込み可能にするために mutable を導入しました。

指定されたミュータブルは、コンパイラとリーダーの両方に、安全であり、メンバー変数が const メンバー関数内で変更される可能性があることを通知します。

于 2013-06-28T12:31:09.630 に答える
5

mutableは、主にクラスの実装の詳細で使用されます。クラスのユーザーはそれについて知る必要はありません。したがって、メソッドはconstである必要があると考えています。ミューテックスを可変にする例は、標準的な良い例です。

于 2008-09-19T20:05:22.977 に答える
5

あなたの使用はハックではありませんが、C++ の多くの場合と同様に、mutable、ずっと前に戻って const であってはならないものを非 const としてマークしたくない怠惰なプログラマーにとってハックになる可能性があります。

于 2008-09-19T20:20:49.630 に答える
2

Mutable changes the meaning of const from bitwise const to logical const for the class.

This means that classes with mutable members are longer be bitwise const and will no longer appear in read-only sections of the executable.

Furthermore, it modifies type-checking by allowing const member functions to change mutable members without using 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.
}

See the other answers for more details but I wanted to highlight that it isn't merely for type-saftey and that it affects the compiled result.

于 2014-07-24T19:50:19.040 に答える
1

古典的な例(他の回答で述べたように)と、これまでキーワードが使用されているのを見た唯一の状況は、キャッシュがクラスのデータメンバーとしてではなくクラスのデータメンバーとして実装されているmutable複雑なメソッドの結果をキャッシュするためのものです。Getメソッド内の static 変数 (複数の関数間で共有するため、または単純なクリーン度のため)。

一般に、キーワードを使用する代わりに、通常はメソッドまたはトリックmutableの静的変数を使用します。const_cast

別の詳細な説明はこちらにあります。

于 2011-05-16T12:32:31.973 に答える
1

場合によっては (不適切に設計されたイテレータなど)、クラスの主要な「状態」に実際には影響しない、カウントまたはその他の付随的な値をクラスで保持する必要があります。これは、ほとんどの場合、ミュータブルが使用されている場所です。mutable がなければ、デザインの const-ness 全体を犠牲にする必要があります。

私にとっても、ほとんどの場合、ハックのように感じます。非常に少数の状況で役立ちます。

于 2008-09-19T20:03:04.573 に答える
1

ミュータブルを使用する最良の例の 1 つは、ディープ コピーです。コピー コンストラクターではconst &obj、引数として送信します。したがって、作成される新しいオブジェクトは定数型になります。この新しく作成された const オブジェクトのメンバーを変更する場合 (ほとんどの場合は変更しませんが、まれに変更する場合があります)、それを として宣言する必要がありますmutable

mutableストレージ クラスは、クラスの非 static 非 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、コンパイラはエラーをスローします。

于 2015-01-25T18:19:03.073 に答える