mutable、volatileの使用

4300 ワード

本文は転載しますhttp://blog.csdn.net/tht2009/article/details/6920511
       (1)mutable
       C++において、mutableはconstの制限を突破するために設けられています.mutbleで修飾された変数は、常に可変の状態になります.たとえ1つのconst関数であっても、構造体変数またはクラスオブジェクトがconstであり、そのmutbleメンバーは修正されます.
struct  ST

{

  int a;

  mutable int b;

};

const ST st={1,2};

st.a=11;//    

st.b=22;//  
       mutableは、クラスでは非静的データのメンバーだけを修飾することができます.mutableデータメンバーの使用は、const関数がオブジェクトのデータメンバーを修正することができるので、詐欺のように見える.しかし、mutbleキーワードを賢明に使用すると、コードの品質が向上します.不確実なものを使用することなく、ユーザーに詳細を隠すことができます.クラスのメンバー関数がオブジェクトの状態を変えないなら、このメンバー関数は一般的にconstとして宣言されることを知っています.しかし、ある時、私達はconstの関数の中でクラスの状態と関係がないデータの成員を修正しなければならないので、このデータの成員はmutalbeに修飾されるべきです.
class  ST

{

int a;

mutable int showCount;

void Show()const;

…

};

ST::Show()

{

…//    

a=1;//  ,   const           

showCount++;//  

}
 
       constは、一旦変数が修飾されると、強制変換を使わない限り、いかなる場合でもその変数の値は変更されず、意図的にも無意識にも関わらず、constによって修飾された関数も同じであるが、ある関数がconstによって修飾されると、直接または間接的に任意の関数以外の変数の値を変えることができなくなり、このような変更を引き起こす可能性のある関数を呼び出してもだめです.このようなコミットメントは文法的にも厳密に保証されています.このような約束に違反する可能性のある行動はコンパイラでチェックされます.
       mutableの承諾は、ある変数がその変数によって修正されると、この変数は常に可変状態になります.たとえ一つのconst関数においても.これはconstと対称的な定義を形成しています.一つは永遠に不変です.もう一つは永遠に可変です.
       変数や関数がconstであるべきかどうかを見ると、それがconstantやinvariantであるべきかどうかだけでなく、変数がmutbleであるべきかを見ても、それがforever muttiveかどうかを見るだけでいいです.
       ここには3つの問題があります.
       1、なぜ保護クラスのメンバー変数が変更されないのですか?
       2、なぜconstでメンバー変数を保護して、もう一つのmutableキーワードを定義してconstの封鎖ラインを突破しますか?
       3、果たしてconstとmutableの二つのキーワードを使う必要がありますか?
       プロテクトクラスのメンバー変数はメンバー関数では変更されません.モデルの論理が正しいことを保証するために、constキーワードを使って関数内で誤ってクラスオブジェクトが修正された状態を回避するためです.そして、このメンバー関数を使用する全ての場所で、このメンバー関数を使用することによる影響をより正確に予測することができる.mutableはconstの封鎖線を突破するために、クラスの副次的または補助的なメンバー変数を随時変更することができます.constとmuttableのキーワードを使っていないのはもちろんです.constとmutbleのキーワードはモデリングツールにより多くの設計制約と設計柔軟性を与えただけです.そして、プログラマはより多くの論理検査問題をコンパイラとモデリングツールに任せて、プログラム員の負担を軽減します.
(2)volatile
       constのように、volatileは一つのタイプの修饰符です.volatile修飾のデータは、コンパイラが実行期間をレジスタの最適化に預けてはいけません.この特性は、マルチスレッド同期、中断、ハードウェアプログラミングなどの特殊なニーズを満たすためである.このキーワード宣言の変数に出会うと、コンパイラは変数にアクセスするコードを最適化しなくなり、特殊なアドレスに直接アクセスすることができます.
       volatileはもともと「変わりやすい」という意味ですが、この説明はちょっと誤解していますので、「元のメモリアドレスに直接アクセスする」と解釈したほうがいいと思います.「変更しやすい」とは、通常の変数に対してコンパイラ(最適化機能)の未知の変更がある場合(すなわちコード割当を実行することによってその値を変更する場合ではない)ではなく、マルチスレッド、中断などの外在的要因によるものです.コンパイラは最適化を行う時、いくつかの値を取る時に、直接レジスタからアクセスします.メモリから取得するのではなく、この最適化はシングルスレッドのプログラムに問題がありません.しかし、マルチスレッドプログラムでは、複数のスレッドが同時に実行されるため、ある共通の変数がスレッドによって変更されている可能性があります.このとき、残りのスレッドのレジスタの値は古いですが、このスレッド自体はまだ知られていません.変更されていないと思いますが、レジスタから取得すると、プログラム実行に未定義の動作が発生します.volatileで修飾した変数が「変わりやすい」というわけではありません.外因がないとvolatileで定義しても、それは変わりません.volatile修正を加えた変数は、コンパイラがその関連コードに対して最適化を行いません.対応コードを生成して直接に元のメモリアドレスにアクセスします.
       一般に、volatileは以下のようなところに使われています.
       1、サービスプログラムを中断して修正した他のプログラムを検出する変数にvolatileを追加する必要があります.
       2、マルチタスク環境で各タスク間で共有するフラグにはvolatileを追加しなければならない.
       3、メモリマッピングのハードウェアレジスタも、通常はvolatileを追加して説明します.その読み書きには毎回違った意味があります.
       このキーワードを使った例は以下の通りです.
volatile int i=10;

int a = i;

...

//    ,         , i     

int b = i;
 
       volatileはiがいつでも変化する可能性があると指摘していますが、それを使うたびにiのアドレスから読み取らなければならないので、コンパイラで生成されたアセンブルコードはiのアドレスからデータを読み出してbに入れます.最適化の方法は、コンパイラがiからデータを読むコードの間に二回コードがiに対して操作されていないことを発見したため、iから読み返すのではなく、前回読んだデータ(すなわち10)をbに自動的に入れます.このようにして、iがレジスタ変数であるか、ポートデータを示すとエラーが発生しやすくなるので、volatileは特殊アドレスへの直接アクセスを保証することができる.
//addr volatile  
addr=0x57; 
addr=0x58;
       上記の2つのステートメントが外部のハードウェアに対して異なる動作を実行する場合、コンパイラは通常のプログラムに対応するように、上記のステートメントを最適化することができません.「addr=0 x 58」として、第1のステートメントを無視します.つまりマシンコードだけを生成します.コンパイラは逐一コンパイルしてマシンコードを作成します.
       volatileは常に最適化に関連しています.コンパイラにはデータフロー分析という技術があります.分析プログラムの中の変数はどこで値を付けますか?どこで使っていますか?しかし、これらの最適化はプログラムに必要なものではない場合があります.volatileキーワードでこれらの最適化を禁止することができます.1、2つの操作の間にvolatile変数をレジスタにキャッシュすることができません.マルチタスク、中断などの環境で変数が他のプログラムに変更される可能性がありますが、コンパイラ自体は分かりません.volatileはコンパイラにそのような状況を教えています.2、定数合併、定数伝播などの最適化をしないので、下記のコードのように、ifの条件は無条件真とはみなされません. 
volatile int i = 1; 
  if (i > 0)
       ... 
       3、volatile変数に対する読み書きは最適化されません.変数に対して値を賦課していますが、その後は使われていません.コンパイラはしばしばその割当操作を省略できますが、メモリMapped IOの処理はこのように最適化されません.