読者です 読者をやめる 読者になる 読者になる

本当に怖いC++erとC++という糞言語

かつて、ゲームプログラミングはアセンブリが主流で、8bitのCPUは掛け算や割り算すらないものでした。割り算がないCPUっていつの時代だよ、っていう人たちもおりますが、ゲームボーイアドバンスに搭載されているARM7TDMIは除算の命令を持っていません。(故に除算を書くと死ぬほど遅いので、乗算で代用したりする)

 

また、浮動小数に対する演算ユニットを持っていないハードウェアもあります。ニンテンドーDSに搭載されているARM946E-Sですら、浮動小数演算ユニットはありません。(CPUの機能としてはオプションで存在する)そのために固定小数点といった技術もあるわけですが、古くさい話です。

これらはCとC++の機能を駆使していかにパフォーマンスを出すかを余儀なくされた時代です。

 

さておき、最近はスマートフォンでのゲーム開発も進化しており、C++iPhoneAndroidの両方で動くということもあり、C++でのゲーム開発の需要が高まっている中で、それでもC++は人的な最適化(適切な使い方)を余儀なくされる言語であります。そこで「こんなC++erは嫌だ」という話をまとめてみました。

(決して他の言語が適切な使い方をしないでいい、という論旨では無い)

 

1. dynamic_castとstatic_castの違いが分かっていない

「dynamic_castとstatic_castわかる?」

「あー、あの動的なキャストと静的なキャストのことね」←まるでわかってない

 

RTTI(Run-Time Type Information)の関する知識が無いままにC++をプログラミングしているケース。

他の言語においては、型情報を普通に備えている訳だが、

C++はCの規格上動的に型情報を備えていないケースがある。

(オーバーヘッドに対して明示的にOFFにされるケースがある)

RTTIが有効であれば型情報を備えているが、これらのcastが何を意味しているかわからない場合はC++を使えていないと思って良い。

C++では適切に型情報を理解しなければcastは使いこなせない。

 

特にstatic_cast/reinterpret_castは型情報を騙して(強制して)本来取り出せない筈の情報(例えば、privateやprotectedなメンバ変数)を取り出すことができる手段の1つとしてとても重要なものだ。

(ただし、この動作は言語仕様としては未定義であり、処理系としてそういったコードを生成するケースが多いというだけ、ということに注意)

SDK側でインスタンスを既に作成しているケースなどでは、こちらで継承をしてそれらを作り直すことは難しい。

だが、メモリ上にある数字は防御されることなくプログラマによって書き換えられる。

 

protectedであれば継承を使う。

http://ideone.com/bH5rGd

 

privateであれば強制キャストを使い、同じアライメントにある値を書き換える。

http://ideone.com/aHQ8st

 

そして、このことはhackされたiPhone/Androidは常にメモリを書き換えられて不正をされる可能性を秘めている事と同義だし、そういう意識を持つべきだ。(C/C++でなくても、メモリ空間は書き換えられるが)

 

2.mapとhash_mapの違いがわかっていない

 「mapとhash_mapのアルゴリズムわかる?」

「あー、mapね、mapは使った事があるよ」←まるでわかってない

 

 二分探索とハッシュテーブルの差がわからず、アルゴリズムを理解しないままにC++でプログラミングをしている。

他の言語においては、連想配列や集合などのアルゴリズムとして覆い隠されているケースが多いが、

C++においては、これらの知識を持つべきである。

JavaのHashMap、PerlのHash、Pythonのdict、RubyのHash、JavaScriptのobjectなど他の言語で身近に使われるものであり、

高速化には必須の概念である。

C++ではSTLの規格で二分木のmapを提供するが、別途に実装系の拡張としてのhash_mapや、C++11のようにunordered_mapを提供するケースが多い。

これらの使い分けは必須であり、特性を理解して使うべきである。

また、vectorのアルゴリズムを理解していない場合、reserveを適切に使えないケースがある。

各種のコンテナの速度比較を以下に示す。

http://ideone.com/kzhZsv

mapとunordered_mapになぜ速度差があるのかはアルゴリズムを考えればわかる筈。

 

3.スマートポインタ(弱参照)に対する知識がない

「shared_ptrとweak_ptrの使い分けわかる?」

「あー、話には聴いたことあるけど。weak_ptrってのは知らない」←まるでわかってない

 

弱参照を理解していない場合、リファレンスカウンタの所有権と循環参照に苦しめられる。

殆どの言語が(GCで管理されている言語でさえ)弱参照を持っている。

PythonRubyなら、weakref、

Objective-Cなら、__weak、

JavaC#ならWeakReference。

これほど言語に標準的に備わっている機能なのに殆ど知られていないのが弱参照だ。

 C++なら既に死んだポインタを判別できるし、ポインタの消滅を邪魔しない。(弱参照は所有権を持たない)

http://codepad.org/AEC6lcDJ

弱参照という概念は他の言語においてでも標準的に用いられる概念であるのにも関わらず、軽視されている傾向がある。

すべてのプログラマは「所有権」について考える必要がある。

 

まだ他にもあるけど、長くなるのでここまで。 

まとめ 

C++使うならちゃんと勉強しろ。

C++使いたいならちゃんと勉強しろ。

C++使って無くてもちゃんと勉強しろ。

 

 

 

C++は、恐らくちゃんと理解されずに使われてしまっている言語だと思います。

故に多くのケースで糞言語と化しますし、(メモリはリークしまくり、所有権は無茶苦茶、何でも線形探索など)

cocos2d-xを使っていてもC++だけど、メモリ管理はcocosがしてくれるからしなくてもいいよ、安全だよ、なんて話を聴くと悲しくなります。

 

もっとC++の事を知ってからコードを書いても良いのではないでしょうか。

そして、他の言語を使う人たちも言語の事をちゃんと知ってあげて下さい。

C++の設計と進化

C++の設計と進化

 

 C++ Advent Calendar 2013 の記事でした。

http://partake.in/events/91328710-3c7b-436e-bd4e-4d98d88333f9