L.Entisの日記: メンバ変数の m_ 22
日記 by
L.Entis
2つ前のエントリの続き
なぜ m_ をつける必要があるかわからない残念な人へ
既に説明のコメントが付いていますが、要するに
なぜ 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_ を付けることにしました。
m_を付ける派 (スコア:1)
Re: (スコア:0)
うち、っつーか、Javascriptのライブラリでよく見かけんね。
どこが発祥なんやろ
人間にとって自明でないだけではない (スコア:0)
コンパイラにとっても自明ではないので、最適化できるとは限らないという問題がありますね。
http://www.emit.jp/prog/prog_opt0.html [www.emit.jp]
Re:人間にとって自明でないだけではない (スコア:1)
それは「コンパイラが最適化の際にメンバ変数を区別できていない」ことに起因してるから、つまりは「コンパイラが賢くない」からだよねぇ。
ローカル変数にコピーすることで「配慮」を無効化していると捉える方がすっきりする。仮想的にレジスタに読むようなもんでもある。
Re:人間にとって自明でないだけではない (スコア:1)
自明でなければ、コンパイラは賢くても最適化することは出来ません。(但し、最適化オプションでそれらを無視して最適化することも出来るでしょう)
(例)
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 * にすればいいのですが、メンバ関数の場合にはメンバ変数個別に変更があるか指定は出来ませんから、クラスの実装が自明でなければ最適化は出来ませんよね。(インライン関数か、同じファイル内で実装された関数でないと)
修正 (スコア:1)
b += a ; // a の中身は別スレッドで変更されるかもしれない!
は
b += a ; // a の中身は foo 関数か別スレッドで変更されるかもしれない!
です。
Re:人間にとって自明でないだけではない (スコア:1)
foo( &a ) ;
for ( int i = 0; i < 1000; i ++ )
{
wait() ;
b += a ; // a の中身は wait 関数、または別スレッドで変更されるかもしれない!
}
こっちのほうが良かったかもしれないですね。
Re:人間にとって自明でないだけではない (スコア:1)
あと、元コメのリンク先のコードは途中で何も呼んでいないので、そのコメントはメンバを区別していないのが原因てのの反論にはなってないかもだ。
Re:人間にとって自明でないだけではない (スコア:1)
int* p;
void foo(int*a){p=a;}
void wait(void){(*p)+=1;}
こういうコードが書ける以上、関数の中身が自明で無い限り、ローカル変数であったとしてもポインタを渡すと(例:foo)、(それ以降、別の関数であっても、ポインタが渡されなくとも)関数が呼び出されたら(例:wait)、その変数の値参照はレジスタにロードしなおさなければならないという指摘をしたかっただけです。
(これは元のコメントのリンク先の中のいくつかは、根源的にこれと同じような理由だと思います)
Re:人間にとって自明でないだけではない (スコア:1)
でも元コメのリンク先はそうじゃない。
関数の中身を精査可能であるにもかかわらず最適化していない例だ。
該当しない例を反例に出すのは無意味だよ。
もちろん、断言こそしていないが「追いかけないの?」と書いたように関数の中身もコンパイラにとって自明であるという前提があるし、そうでない場合についてはそもそも何も言っていない。それでも「関数の中身が精査可能でない」場合について主張したところで、その場合自体が別の、配慮すると性能が改善する可能性がある条件の一つでしかない以上、精査可能であることが前提のこのケースとは別だ。
繰り返すけど、実際の最適化がそうなっているならそれに従うしかないけれど、そのケースがどれだけ一般的なものであってもそれは飽くまでそのコンパイラでコンパイルする場合の話であって、あるコンパイラの一実装を以って最適化の可能性を論じるのは違うと言っているのだよ。
Re:人間にとって自明でないだけではない (スコア:1)
それは違います。
今回、私はわかりやすく関数コールを例に挙げましたが、リンク先は「外から見れるポインタの先にあるデータは volatile になる」という意味のことを書いています。
これ自体は言語仕様ではありませんが(volatile を明示はしていないので)考え方としては間違えてはいません。
例えば、グローバルな場所に static でない
T* p;
を書いて、p に(例えばローカルな)Tオブジェクトのポインタを代入してしまうと(私の例では関数に渡すと、ですが)、そのTオブジェクトはそれ以降 volatile 扱いになります。(少なくとも MS-C++コンパイラでは)
なぜなら、ある処理中の「p のポイント先への変更」は、全てのリンケージが解決した後出ないと言語的には確定できないからです。
(任意の別ソースで extern で p の先の内容を書き換えられるため)
その様な事情があるため、MS-C++ では自動的に volatile 相当の扱いをしているものと思われ、リンク先の記事は同様の意味のことが書かれています。
リンク先の前半の例3つではメンバ変数が例として用いられていますが、その後にわざわざグローバルなポインタに代入すると最適化がかからないという例まで挙げられています。
(因みにメンバ変数の場合は、関数がインラインで展開されない限り、this ポインタがどのような事情にあるかわかりません)
従って、例として関数の中身を精査可能なのは、(同じファイル内に書かれていると思われる)リンク先の前半の例3つだけで、しかも、説明している内容としては、クラスの定義と main 関数の実装が同一ファイルにある必要はないため、むしろ「関数の中身を精査可能」というのは「例として不完全」と言う意味にしかなりませんよ。
(繰り返しになりますが、後半にはわざわざグローバルなポインタに代入する例まで挙げられているのですから、記述者の意図は私の意図とも合致していると思います)
Re:人間にとって自明でないだけではない (スコア:1)
残念な人です。 (スコア:0)
Re:残念な人です。 (スコア:1)
うっかり、関数引数やローカル変数 'a' のつもりで a と書いてしまっても、実際には関数引数やローカル変数に a が無いと(あるいはタイプミスなどで)コンパイルエラーは発生しないまま、誤ったコードが実行されることになります。
一応、簡単な例 (スコア:1)
private int abc ;
void hoge() {
int adc = 1 ; // abc = 1 のつもり
//...
abc = foo( abc ) ; // ローカル変数のつもりが...
のようなコードは private でも意味がありません。
というか、クラス内でプレースホルダーとしての意味も無いのに(確かに一応クラス内でもgetter/setterを使う意味はあるが)、メンバ変数へのアクセスを禁止するのは前述以外の意味はあまりないかと思います。(逆にクラス内だと getter/setter を通さないメンバ変数への変更が必要な操作を実装する必要がある場合もある(だからメンバ関数として定義して外からは直接触れないようにする意味がある))
(もうちょっと private の意味を考えてあげて)
勿体付けてるからどんな話かと思ったら (スコア:0, 荒らし)
なんだそんなことか。
それはそれで有用だろうが、コーディングの流儀以上のものではないな。
Re:勿体付けてるからどんな話かと思ったら (スコア:1)
もちろん、m_ である必要はありませんが。
Re:勿体付けてるからどんな話かと思ったら (スコア:1)
「上」ってアクセサ使うって話?
俺はメンバメソッドからメンバ変数にアクセサ使うのには賛同しないよ。
メンバである意味がないじゃないか。
どっちかというと、メンバ変数くらい把握しようよって立場。
もちろん、これにはクラスの規模や複雑さを容易に把握できるくらいに抑えておくという意味も含まれる。
とはいえ、ネーミングルールは逆にメンバ変数以外を変えてるかなぁ...。
ローカル変数は短く数文字で、グローバルシンボルは頭大文字らくだとかみたいな感じで。
Re:勿体付けてるからどんな話かと思ったら (スコア:1)
いや、そのレス
基本同意
メンバ変数に m_ を付けると言う手法以外、目的も方法論も私と同じ考えだと思います。
Re:勿体付けてるからどんな話かと思ったら (スコア:1)
繰り返しになりますが、当然 m_ で無ければならないという趣旨のものでもありません。
上のコメントに付いても、メンバ変数と名前がかぶらないような名前付けにするのであれば、意味は m_ をつけるのと同じことです。
要するにそれだけのことです。
Re: (スコア:0)
> 要するにそれだけのことです。
クラス変数、ローカル変数、インスタンス変数の区別がつくようにネーミングルールがあるのは良いことだ
ってことですよね?
java開発でこれが無いコーディングルールの仕事ってみたことないので(名前の規則はイロイロだけど)そんなにしゃちほこばって言うほどのことではないと思うんですがC++だとそうも行かなかったりするのかしらん?
#C++は自分がルール決める側ばっかりだったんで他の経験がたりないのだよなぁ<<自分
Re:勿体付けてるからどんな話かと思ったら (スコア:1)
最近は、
「IDEはこれらを区別して、色別で表示するから、名前による区別は不要!!」
という所も増えているそうです。
naming rule による縛りは「その変数はどのような変数か?」を理解したプログラマーだけで構成されたプロジェクトでないと、間違った名前付けをして逆にプログラムが混乱の渦に巻き込まれます。
一度この辺りがまるで判っていない初心者がプロジェクトに紛れ込んでいて凄いことになったことが…
なので 「IDEを使えば、名前にルールを付ける必要無しっ!!!」という感覚も判らなくはありません。
fjの教祖様