パスワードを忘れた? アカウント作成
322424 journal

L.Entisの日記: メンバ変数の m_ 22

日記 by L.Entis
2つ前のエントリの続き

なぜ m_ をつける必要があるかわからない残念な人へ
既に説明のコメントが付いていますが、要するに

int a = 1 ;
add( 2 ) ;
int b = a ;

の b の値は自明ですが(関数に a のポインタを渡さない限り必ず b == 1)

m_a = 1 ;
add( 2 ) ;
int b = m_a ;

の b の値は自明ではありません。(コーディングした人の意図とは別に)
まあ、こういう部分の意味とか、コード上の読みやすさ(追いやすさ)を気にしない人は const も使わないんでしょうね。

因みに、結論としては C++ と同じような基準で m_ をつけてコーディングすることにしました。
簡単に言うと、構造体のようなクラスのメンバには m_ は付けないで、そうではないクラスのメンバには基本的に m_ を付けることにしました。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by miyuri (33181) on 2011年05月03日 17時13分 (#1946328) 日記
    無かったら、メンバ変数とメソッド内変数が合わさって頭がおかしくなって死ぬ。
    • by Anonymous Coward
      うちの方だと、「_」をつけてるかな。
      うち、っつーか、Javascriptのライブラリでよく見かけんね。
      どこが発祥なんやろ
  • by Anonymous Coward on 2011年05月03日 12時09分 (#1946136)

    コンパイラにとっても自明ではないので、最適化できるとは限らないという問題がありますね。
    http://www.emit.jp/prog/prog_opt0.html [www.emit.jp]

    • それは「コンパイラが最適化の際にメンバ変数を区別できていない」ことに起因してるから、つまりは「コンパイラが賢くない」からだよねぇ。
      ローカル変数にコピーすることで「配慮」を無効化していると捉える方がすっきりする。仮想的にレジスタに読むようなもんでもある。

      親コメント
      • いえ、コンパイラが賢いかどうかではなく、クラスの中身や(呼び出される)関数の実装が自明であるかどうか、です。
        自明でなければ、コンパイラは賢くても最適化することは出来ません。(但し、最適化オプションでそれらを無視して最適化することも出来るでしょう)

        (例)
        int a = 0, b = 0 ;
        foo( &a ) ;
        for ( int i = 0; i < 1000; i ++ )
        {
            b += a ; // a の中身は別スレッドで変更されるかもしれない!
        }


        この事情は(静的変数やグローバル変数でも同様ですが)クラスの変数も同様です。
        しかし流石に volatile 指定しないと、上のような場合には最適化されるでしょうけど、以下の場合には特別に無視するオプション(コンパイラ依存)を指定しないと最適化はしてくれないでしょう。

        for ( int i = 0; i < 1000; i ++ )
        {
            foo( &a ) ; // 引数は int* で const int* ではない
            b += a ; // a の中身は別スレッドで変更されるかもしれない!
        }

        これは const int * にすればいいのですが、メンバ関数の場合にはメンバ変数個別に変更があるか指定は出来ませんから、クラスの実装が自明でなければ最適化は出来ませんよね。(インライン関数か、同じファイル内で実装された関数でないと)
        親コメント
        • by L.Entis (21733) on 2011年05月03日 14時25分 (#1946219) ホームページ 日記
          2つ目の

          b += a ; // a の中身は別スレッドで変更されるかもしれない!



          b += a ; // a の中身は foo 関数か別スレッドで変更されるかもしれない!

          です。
          親コメント
        • 連投続きになってしまいますが、2つ目の例は

          foo( &a ) ;
          for ( int i = 0; i < 1000; i ++ )
          {
              wait() ;
              b += a ; // a の中身は wait 関数、または別スレッドで変更されるかもしれない!
          }

          こっちのほうが良かったかもしれないですね。
          親コメント
          1. 別コンテキストの話をするなら何らかのバリアがないとたまたま動くコードを吐くだけだと思うがなー。関係ないけどそういうバリアの事を考えるとスレッドがあるってOSの設計としてどうかなぁ...と思う。
          2. その例ならむしろ書き込まれているものだと考えるべき。そうでなくてfooがメンバなだけならfooの中身は追わないの?って話にならないかなー。今のコンパイラはその場の自明しか見てないからそれに合わせるって話ならわかるし同意もするが、そのメンバ変数が変化するかどうかが自明かどうかって話なら同意できないな。

          あと、元コメのリンク先のコードは途中で何も呼んでいないので、そのコメントはメンバを区別していないのが原因てのの反論にはなってないかもだ。

          親コメント
          • 言いたいことが良くわからないのですが、私の例はメンバ関数とかそういうものは一切関係ないです。(ただ、メンバ関数だと暗黙にメンバ変数を含むポインタを渡しているのと同じことだという意味では同じですが)

            int* p;
            void foo(int*a){p=a;}
            void wait(void){(*p)+=1;}

            こういうコードが書ける以上、関数の中身が自明で無い限り、ローカル変数であったとしてもポインタを渡すと(例:foo)、(それ以降、別の関数であっても、ポインタが渡されなくとも)関数が呼び出されたら(例:wait)、その変数の値参照はレジスタにロードしなおさなければならないという指摘をしたかっただけです。
            (これは元のコメントのリンク先の中のいくつかは、根源的にこれと同じような理由だと思います)
            親コメント
            • でも元コメのリンク先はそうじゃない。

              関数の中身を精査可能であるにもかかわらず最適化していない例だ。
              該当しない例を反例に出すのは無意味だよ。

              もちろん、断言こそしていないが「追いかけないの?」と書いたように関数の中身もコンパイラにとって自明であるという前提があるし、そうでない場合についてはそもそも何も言っていない。それでも「関数の中身が精査可能でない」場合について主張したところで、その場合自体が別の、配慮すると性能が改善する可能性がある条件の一つでしかない以上、精査可能であることが前提のこのケースとは別だ。

              繰り返すけど、実際の最適化がそうなっているならそれに従うしかないけれど、そのケースがどれだけ一般的なものであってもそれは飽くまでそのコンパイラでコンパイルする場合の話であって、あるコンパイラの一実装を以って最適化の可能性を論じるのは違うと言っているのだよ。

              親コメント
              • 関数の中身を精査可能であるにもかかわらず最適化していない例だ。

                それは違います。

                今回、私はわかりやすく関数コールを例に挙げましたが、リンク先は「外から見れるポインタの先にあるデータは volatile になる」という意味のことを書いています。
                これ自体は言語仕様ではありませんが(volatile を明示はしていないので)考え方としては間違えてはいません。

                例えば、グローバルな場所に static でない

                T* p;

                を書いて、p に(例えばローカルな)Tオブジェクトのポインタを代入してしまうと(私の例では関数に渡すと、ですが)、そのTオブジェクトはそれ以降 volatile 扱いになります。(少なくとも MS-C++コンパイラでは)
                なぜなら、ある処理中の「p のポイント先への変更」は、全てのリンケージが解決した後出ないと言語的には確定できないからです。
                (任意の別ソースで extern で p の先の内容を書き換えられるため)
                その様な事情があるため、MS-C++ では自動的に volatile 相当の扱いをしているものと思われ、リンク先の記事は同様の意味のことが書かれています。

                リンク先の前半の例3つではメンバ変数が例として用いられていますが、その後にわざわざグローバルなポインタに代入すると最適化がかからないという例まで挙げられています。
                (因みにメンバ変数の場合は、関数がインラインで展開されない限り、this ポインタがどのような事情にあるかわかりません)

                従って、例として関数の中身を精査可能なのは、(同じファイル内に書かれていると思われる)リンク先の前半の例3つだけで、しかも、説明している内容としては、クラスの定義と main 関数の実装が同一ファイルにある必要はないため、むしろ「関数の中身を精査可能」というのは「例として不完全」と言う意味にしかなりませんよ。
                (繰り返しになりますが、後半にはわざわざグローバルなポインタに代入する例まで挙げられているのですから、記述者の意図は私の意図とも合致していると思います)

                親コメント
              • 念のため補足しますが、元コメントのリンク先の例では、main 関数側からはメンバ関数の中身が最適化可能か確定的ですが(呼び出すオブジェクトが外から変更されうるのか分かる)、呼び出されるメンバ関数側では this ポインタがどのような状態のポインタであるかは不明であるため(インライン展開されないコード生成としては)volatile な扱いになる(その上での最適化が行われる)コンパイラがあっても不思議ではありません。
                親コメント
  • by Anonymous Coward on 2011年05月03日 13時13分 (#1946177)
    メンバに対しては getter/setter を定義して常にそれ経由でアクセスしてるから特に問題を感じないですねぇ。

    setA( 1 );
    add( 2 ) ;
    int b = getA();

    • クラス内からであってもメンバ変数へは getter/setter のみを使う、というのであったとしても、問題があります。

      うっかり、関数引数やローカル変数 'a' のつもりで a と書いてしまっても、実際には関数引数やローカル変数に a が無いと(あるいはタイプミスなどで)コンパイルエラーは発生しないまま、誤ったコードが実行されることになります。
      親コメント
      • class T {
        private int abc ;
        void hoge() {
            int adc = 1 ; // abc = 1 のつもり
            //...
            abc = foo( abc ) ; // ローカル変数のつもりが...

        のようなコードは private でも意味がありません。
        というか、クラス内でプレースホルダーとしての意味も無いのに(確かに一応クラス内でもgetter/setterを使う意味はあるが)、メンバ変数へのアクセスを禁止するのは前述以外の意味はあまりないかと思います。(逆にクラス内だと getter/setter を通さないメンバ変数への変更が必要な操作を実装する必要がある場合もある(だからメンバ関数として定義して外からは直接触れないようにする意味がある))
        (もうちょっと private の意味を考えてあげて)
        親コメント
  • なんだそんなことか。
    それはそれで有用だろうが、コーディングの流儀以上のものではないな。

    • 上参照。
      もちろん、m_ である必要はありませんが。
      親コメント
    • あと、別に勿体つけたわけではなく、大したことでもないのに(そもそも単なる流儀の話をしています)忙しくて書く暇(書く気が起こる暇)が無かっただけです。

      繰り返しになりますが、当然 m_ で無ければならないという趣旨のものでもありません。
      上のコメントに付いても、メンバ変数と名前がかぶらないような名前付けにするのであれば、意味は m_ をつけるのと同じことです。
      要するにそれだけのことです。
      親コメント
      • by Anonymous Coward

        > 要するにそれだけのことです。
        クラス変数、ローカル変数、インスタンス変数の区別がつくようにネーミングルールがあるのは良いことだ
        ってことですよね?

        java開発でこれが無いコーディングルールの仕事ってみたことないので(名前の規則はイロイロだけど)そんなにしゃちほこばって言うほどのことではないと思うんですがC++だとそうも行かなかったりするのかしらん?

        #C++は自分がルール決める側ばっかりだったんで他の経験がたりないのだよなぁ<<自分

        • java開発でこれが無いコーディングルールの仕事ってみたことないので(名前の規則はイロイロだけど)そんなにしゃちほこばって言うほどのことではないと思うんですが

          最近は、
          「IDEはこれらを区別して、色別で表示するから、名前による区別は不要!!」
          という所も増えているそうです。

          naming rule による縛りは「その変数はどのような変数か?」を理解したプログラマーだけで構成されたプロジェクトでないと、間違った名前付けをして逆にプログラムが混乱の渦に巻き込まれます。
          一度この辺りがまるで判っていない初心者がプロジェクトに紛れ込んでいて凄いことになったことが…

          なので 「IDEを使えば、名前にルールを付ける必要無しっ!!!」という感覚も判らなくはありません。

          --
          fjの教祖様
          親コメント
typodupeerror

「毎々お世話になっております。仕様書を頂きたく。」「拝承」 -- ある会社の日常

読み込み中...