逆に、コードの流れの中で、インクリメントした後の結果しか使わないのであれば、前置でも後置でもCPUのコストは変わりません。仮にインクリメント演算子の中で70,000サイクルの処理がかかるとしても、インクリメント後の値しか使わないのであれば、前置でも後置でも等しくその負荷が計上されることになります。計測サンプルダラダラとアセンブラコード貼ってる暇があったら実測値をだしてみろよ!投資家は数字でしか動かねーんだよ!というYコンビネーターマインドの方もいるかもしれません。以下のサイトではインクリメントのコストを複数のコンパイラで比較検証されていますので一例として参考にして頂ければと思います。典型的なfor文を前置後置それぞれのインクリメントを使用する形で作成。int/iterator/適当に重めのクラスの三種それぞれを対象。最適化により返り値計算が消えることを考えforの真偽判定でインクリメントの返り値を使用するものも用意。以上をそれぞれ100,000,000回実行をさらに10回繰り返しかかった時間の平均をとる。全計測の結果は元サイトよりご確認下さい。余談ではございますが闇夜のC++のサイト運営に2億くらい出してもいいよというエンジェル投資家の方がいらっしゃいましたら、ご連絡下さい。我々は現実の世界で実際にコードを書くプログラマです。そこには机上の論理以外の様々な要素を考慮する必要があります。結論を出す前に我々が向き合わなければいけない現実的な考慮事項は4つあります。第1に、一時オブジェクトの検証結果ですが、インライン展開が効かない、前置インクリメントにアドバンテージのあるシチュエーションにするには、リンク時最適化の効かないコンパイラ上で、ユーザー定義のインクリメントオペレータ演算子をcpp側に書く必要があります。インクリメント演算子もデクリメント演算子もクラス書くたびに必ず用意してます(しかもヘッダーファイルじゃなくcpp側に!)という人は別として、一般的に最も多く呼び出されるインクリメント演算子は組み込みのint、もしくはSTLのイテレータに対してではないでしょうか。その場合、やはり先程の検証でもあったように、コンパイラの素晴らしい最適化により、前置と後置のコストの違いは限りなく少なくなります。第2に、インクリメント演算によって発生するなんらかのストールを回避するにはインクリメント前の値を使うようなコードを心がける必要があります。インクリメント演算子を式中で使ったり、インクリメント後もインクリメント前の値に対する処理を書くという感じです。可読性の観点からすると、あまり推奨されるようなコードではありません。第3に、ストールのコストは肌で感じづらいという問題があります。処理に直接関係するコストは、例えばアセンブラコードの出力を見れば把握できます。余分なオブジェクトが生成されているな、とか、最適化されているな、とか。あるコードを書いた時にその部分の処理によってCPUにストールが発生しているかというのは極めて把握が難しい問題です。第4に、しかし、それでも、いかんともしがたく、アーキテクチャに引っ張られることがあるという点です。中盤でも触れましたが、一部のアーキテクチャにおいてはストールの発生を抑えなければロクに処理が動かないというケースがあるのも事実です。いうアーキテクチャと向き合わなければならない場合、全く一切選択の余地なく、可読性うんぬん以前に、方針を決めざるをえない場合もあります。CPUのアーキテクチャへの理解と、コンパイラの挙動、そして現実にコードを書く我々、この3つを勘案した上で、実際に前置インクリメントを使うべきか、後置インクリメントを使うべきか、また今の時代そういうことは気にしないべきか、考える必要があります。私は、後置インクリメントを使っている事を指摘した際に「だってゲームエンジン・アーキテクチャに書いてあったからなんとなく」という答えをするプログラマが増えない事だけを願っています。teppynullptrはc++11から追加された最も有益な機能の一つです。今、このアーティクルを書いている時代は2015年ですが、未だにnullptrの使えない環境が存在します。信じられないでしょうが、例えばコンソールゲームの開発環境では未だにc++03相当の機能しかないコンパイラを相手にしなければいけないことがあります。このアーティクルはプログラマとしての尊厳を奪われている一部の人向けに存在しているのであり、LLVMを使えるような、いわゆる普通の環境にいる人には全く意味の無いものであることを最初に宣言させて下さい。nullptrが存在しなかったころnullを表すものは単に数値の0でした。それは全く不十分なもので、単に0が書かれていてもコンパイラはポインタなのか数値なのかを把握できません。COMPLETEの一節にこんな言葉がります。第二版つまり言語が用意してくれていない機能や有効な思想は、言語に無いからといって諦めるのではなく、なんとか実現できる方法がないか考えようということです。レガシーな環境でもnullptrが使えるようにオリジナルで実装しましょう。nullptr;これで数値に代入できない型の完成です。無名クラスで直接nullptrを定義してます。環境によっては名前をつけて別途インスタンスを生成しないとコンパイルエラーになるのでその場合はもうちょっとコードを書く必要があります。第二版にこのnullptrの実装が書いてありましたが、第三版ではバッサリカットされています。teppyオブジェクトの生成と破棄には莫大なコストがかかることがあります。CPUによるメモリ割り当て、コンストラクタ・デストラクタの挙動等、オブジェクトが一つ生成され、破棄されるまでのコストは無視できません。あなたの目の届かないところで提供されたサードパーティ製のライブラリのクラスでは、コンストラクタ内で自分のメンバからCRC値を生成して勝手にツイートする実装になっているかもしれません。コンパイラも、そんな我々の気持ちを汲んで、なるべく一時オブジェクトの生成を抑制=最適化しようとしてくれます。Optimization)です。関数の戻り値を値で返す時に発生する一時オブジェクトをコンパイラは可能な限り消し去ります。0;}一切なんの最適化も行われないと仮定すると上記のコードは3回Hogeオブジェクトを生成することになります。実際には1度しかコンストラクタ・デストラクタは呼ばれません。現代の主要なコンパイラにとってこの最適化は当たり前となっており、gccでもclangでも、armccでもRVOは効きます。Wandboxで際に実行してみると、コンストラクタ・デストラクタがそれぞれ一度しか呼ばれないことが確認できます。C++の第四章「戻り値の最適化」にも説明があります。Idiomこの最適化を念頭に入れておけば、値を返す関数を可読性の高いコンストラクタ呼び出しとして使うことができます。Idiomと言います。Idiomsにも載っていますので参照すると良いでしょう。teppy順調に動いていたはずのプログラムが突然ダンプを吐いてクラッシュする・・・。悲劇としか言えません。ある調査機関によると成人男性の約89%がこの不幸に出会っているという報告もあります。[要出典]最も多く、そして最も簡単に発生する原因としてnullポインタアクセスが挙げられます。以下のコードを見てみましょう。nullptrに対してメソッドを呼ぶ。nullptrに対してメソッドを呼ぶ。0;}このプログラムは実行するとすぐさまクラッシュします。問題はどこでアクセスエラーが発生するか?です。1;の部分なのですが、なぜメソッド呼び出しC++car->accel();1car->accel();ではなく、その内部の処理まで進むのでしょうか。理由はc++のアーキテクチャにあります。あるクラスのメソッド、あるインスタンスのメソッド呼び出しとは、内部的には通常の関数にインスタンスのポインタを渡しているだけなのです。暗黙の引数としてthisが渡されるイメージです。0;}というコードに近いイメージになります。1;だという事がよりイメージしやすいのではないかと思います。C++でコーディングしていると、ポインタを操作する前にnullチェックをするコードを書くケースが多いのですが、上記を踏まえ、こんなことを考えるかもしれません。1;}こんな感じでメソッドの中でチェックすれば、呼び出し側が、色々な場所で呼び出すかわりにnullチェックしなくて済む。このコードは再びアクセスエラーになる可能性があります。?一見、ちゃんとnullチェックもしているように見えます。C++の規格ではメソッド中でthisがnullではないことが保証されているため優秀なコンパイラは単にメソッド内でのthisへのnull判定コードを最適化で消し去ります。これが今回のアーティクルのタイトルにもなっているメソッド内でのthisの最適化です。コンパイラオプションによってこの最適化を抑制することもできるのですが、規格に準じたコードではないので書くべきではありません。コラム行儀よく普通にコーディングしてるとメソッドの中でthisを判定したいことは少ないと思います。こんな最適化に関する知識、それほど重要じゃないと思われるかもしれません。あるゲームエンジンを書いていた時のことです。そのゲームエンジンはオブジェクトを生成すると同時に色々な準備を同時に行う必要があったのでファクトリによって生成されました。生成と破棄の詳細についてユーザは知る必要がないのでインスタンスを破棄する時、ユーザーは単に破棄用のメソッドを呼びます。delete演算子にnullポインタを渡しても支障が無いので、この破棄処理もobjがnullかどうか破棄の前に判定させることを強要しない作りにしたほうがシンプルで良いと考えるのです。......メソッドの中でthisによる判定を書いてしまうことが......あるのです......。あなたがもし、ゲームエンジンの実装を任されているエンジニアで、なおかつdeleteがnullポインタを許容する事実を知っている場合は、この最適化の事を覚えておいて損はないでしょう。シンプルでブレの無いエンジンを目指すなら、そもそもオブジェクトの生成はスマートポインタで返すべきかもしれません。teppy2件のコメントポインタをdeleteした後は、対象のポインタの値をnullにした方が安全で良いプラクティスだとされています。null値にクリア毎回deleteする度にクリア処理を書くのは手間です。マクロ化してみましょう。nullptr;}一昔前に良く見かけたマクロです。マクロはプリプロセスで展開されます。展開され、コンパイルエラーもマクロの中とは別の場所を示します。何より名前空間を指定することができません。取り回しが良いとは言いづらいですね。C++でも、かなり最初の方に#defineを使うことへの注意が書いてあります。言い換えれば「プリプロセッサよりコンパイラを使おう」ということです。#defineは、一般には、言語の一部としては扱われません。この事実から問題が起こることもあるのです。nullptr;}引数はTのポインタの参照です。Tのポインタの参照として受けることでnullptrの代入が呼び出し元に反映されます。間違ってT*だけにしてしまうとこの関数の外に戻った時にポインタの値が元のアドレスのままになってしまうので気をつけて下さい。ここで一旦、safe_deleteのことは忘れて、以下の処理の流れを考えてみます。0;}前方参照されただけのHogeという型のポインタのインスタンスを生成して、deleteに渡すコードです。この場合、コンパイラはHogeという型の詳細を知りません。別のファイルでHogeの中身が定義してあるとしても、このソースのコンパイル時にその情報を持っていません。Hogeは前方参照されており、このコード中はポインタでしか扱っていない為、型サイズやメンバ変数の内容を知らなくてもコンパイルできてしまいます。コンパイラによっては警告を出しますが、シンタックスエラーという訳でもないのでコンパイルは通ります。Wandbox上でもコンパイルが通ることが確認できます。不完全型がコンパイルエラーじゃないかというと、実のところ、不完全型をdeleteに渡すことは規格で許可されているからなのですが、但し書きが付きます。独自のdelete演算子を定義していたり、トライバルなデストラクタを持たないクラスの場合、規格の保証外となり、未定義動作になります。未定義動作は、単に不具合の元となりますので絶対にやってはいけません。絶対にやってはいけないことは、絶対にできないように書く必要があります。先ほどのsafe_deleteに不完全型をコンパイルエラーにする仕組みを導入しましょう。ランタイムではなくコンパイル時にエラーにするのが肝要です。nullptr;}不完全型が渡された時に配列の要素が負数になり、そんなものはコンパイラが許さないのでエラーになります。みんな大好きなboost::checked_deleteも同様のコンセプトの元、用意されています。これで無事、完全なsafe_deleteを実装することができました。簡単ですね!実装する際は配列バージョンも用意するのをお忘れなく!その先へ安全なsafe_deleteの実装は完了しました。これはsafe_deleteの実装が安全なのであって、safe_deleteを使う環境が安全になったわけではありません。同僚プログラマがsafe_deleteを呼び忘れるリスク等を考慮するとスマートポインタなどの使用も検討しましょう。teppy以下のようなマクロ定義を見かけることがあります。p;}昔々、マイクロソフトがDirectXのサンプルで上記のようなマクロを定義していたことがあり、恐らく、多くの人がそのサンプルのままで覚えた結果、特に深く考えずに実装しているケースも少なくないのでしょう。ポインタがnullじゃないことを判定した後、deleteするという処理です。delete対象のポインタがnullだった場合、どうなるのでしょう?未定義の動作でしょうか?アクセス例外でハングアップしたり、ニュージャージー州に住む叔母に勝手にメールを送るような動作を引き起こしたりするでしょうか?答えは「何も起こらない」deleteにnullポインタを渡しても安全であると規格にも明記されています。Delete知っている人には当たり前でも、案外知らずにC++を使っている人も多いように感じます。nullかどうかを気にせずポインタをdelete出来るかわりに、今度は別の、気をつけなければいけないことが生じます。自分でdelete演算子を定義する場合です。ユーザー定義のメモリ解放処理}}規格に準ずる為にはnullを許容する実装になっていなければいけません。