適当にweb、っていうかここの文章見てると段落の一文字字下げって結構必要だと思った。
オブジェクト指向はオブジェクト指向っていう名前が悪い
オブジェクト指向が理解できる時っていうのはオブジェクト指向は オブジェクトに指向なんかしてないて気付いたとき。 っていう話。
オブジェクト指向(以下OO)の定義っていうと、まあ、よくわからんけど、 とりあえず、オブジェクト間でメソッドとかでメッセージのやりとりをするみたいな感じだろうか。
で、オブジェクト指向すると、なんか素晴らしくコーディングが楽になるという認識があるような気がする。 っていうか、前の授業で先生がそう言ってたし、まあ、そういう感じがしなくもない。
しかし、そのメッセージを発したり、受け取ったり処理したりとかを 誰がやるかっていう責任所在がはっきりしてるような状況っていうのは、おそらく C で書いたとしても 簡単に書けるような状態じゃないのかと思う。
実際にプログラム書いてて悩まないといけないのは、複雑な処理をどうやって分割するのか、 どうやって抽象化するのか、多数のオブジェクト間でどうやって一貫性を保つかといったような オブジェクト間の関係に関する問題ばかりで、 そういった問題に当たった時、OOだからC言語よりもなんとかなるというものでもない。
-
いくつかOOの利点とかいうものについて考えてみる
カプセル化はOOにしたからといって何とかなるものでもないだろう。 上手な人がやればなんでも隠蔽されるし、下手な人がやれば何でも外に出てくる。
継承は、「オブジェクト指向」という名前のせいで最も誤解を受けている物だと思う。
「オブジェクト指向入門」みたいな文章となると、「継承による差分プログラミング」
みたいな感じで書かれているが、継承による差分が使える状況っていうのは相当少ない。
まず、そもそもふたつのクラスに分割できるんだったら、オブジェクトコンポジションっていうか、 そのまま分割しておけばいいじゃん。みたいな感じだし、プログラミング時において、美しい "is-a関係" が生まれる状況っていうのは 実世界のそれと比べると非常に少ないだろうし。それにも関わらず、「OOの最大の利点は継承による差分」みたいな宣伝をするから、 初心者が継承を使いたがって、結局理解できなくなってしまうんじゃないかと思う
継承が役立つ場合もある、といえば、9割方これじゃないだろうか。
オブジェクト側で動作を決定できる。と言えば、いかにもオブジェクトに指向してる(?)感じである。
しかし、何かがおかしいと思わないのか。オブジェクト側で動作が自動的に決まる。と言っても、 全てをライブラリで実装するとかそういう場合以外、結局オブジェクト側のコードも自分で書かないといけないのである。
"ポリモーフィズムこそOOだ"という意見は間違ってはいないと思うが、その意見は完全にオブジェクト利用側 から見た意見である。オブジェクトを作る側の気持ちを代弁してるとは言い難い。
ポリモーフィズムな感じっていうのは、オブジェクトを何とかする手段なのではなくて、 オブジェクトに対するインターフェースを抽象化する為の手段なのだと思う。 オブジェクトの為のインターフェースではなくて、インターフェースの為のインターフェース、 よくわからんけど。そのへんが何かおかしいんじゃないのか。と。
要するに、OO言語にしたからと言って変わる部分はほんのわずかしか無い。 それは、「ある問題の抽象化に対して高レベルなインターフェースを持っているかどうか。」だ。 気合いを出せば、Cでも書けるし、なんだったら機械語でも書ける(ていうか、全ての言語は結局のところ機械語なんだし)、 ただ、OO言語を使えば、それが少しだけ、楽に、間違い無く書けるということだ。
例えば、C言語で関数ポインタを使うのと、C++でvirtualメソッドを使う場合を考えれば明らかだろう
--
僕は、プログラミングというものは、「物体を〜っていうふうに操作したい…」、っていう問題があったとして、 その問題を細かく、細かく、頭の悪いコンピュータさんでも理解できるように噛み砕いていく作業だと思っている。
オブジェクト指向、っていうと、オブジェクトを楽に操作できるイメージを持ちそうになる。 問題を分割していくという手順を飛ばして、解答へ届いてしまいそうなイメージだ。
もし、そのイメージのせいで、初心者に「OOは万能」と思わせ、混乱させているとすれば、 「オブジェクト指向」という名前が悪いのだと思う。っていう話。
--
とりあえず、誰かに「オブジェクト指向って何?」と聞かれたら、
「別に、普通と何も変わらん」
と、答えるのが良いと思う。
C++初期化リストとリセットメソッド
C++に初期化リストっていうのがある。
class Nanika { int a, b, c; public: Nanika( ) :a(0), b(1), c(2) // 初期化リスト {} };
初期化リストは特に理由が無い限り使わないで、
class Nanika { int a, b, c; void init() { a = 0; b = 1; c = 2; } public: Nanika() { init(); } };
上のように書くほうがいいかと思う。
何らかの理由で、リセット処理みたいなものを行いたいという場合っていうのはあるかもしれない。 そうなった時に、init が実装済みの場合は、init を呼び出すだけでいいけど、 初期化リストで書いた場合は結局 init によく似た物を実装しないといけなくなってしまう。 そして、おそらく冗長性を減らすために結局コンストラクタの初期化リストを消して コンストラクタはinitを呼び出すことになるだろう。 結局2番目のコードの形になってしまうのだ。
class Nanika { int a, b, c; void init( ) { a = 0; b = 1; c = 2; } public: Nanika() { init(); } reset() { init(); } };
必要になった時に書き直せばいいと思うかもしれない、けど
class Nanika { Nanika_Sugoku_Fukuzatuna_Mono member; public: Nanika() :member() {} };
っていうようなクラスがあったとして、これにリセットメソッドを追加しようとしたとき、member にリセットメソッドが実装されてなかったら、memberにもリセットメソッドを実装しないといけなくなってしまう。 で、そのとき、memberも何かを包含していたとしたら、それにもリセットを実装……
っていうふうに、結構な量の「初期化リスト→再初期化メソッド書き換え」を行わないといけなくなってしまう。 で、エンバグに立ち向かう勇気なんてもちろん無いから、僕の場合、
class Nanika { Nanika_Sugoku_Fukuzatuna_Mono *member; public: Nanika() :member( new Nanika_Sugoku_Fukuzatuna_Mono() ) {} reset() { delete member; member = new Nanika_Sugoku_Fukuzatuna_Mono(); } };
っていうふうになってしまうのである。別にこれでもいいんだけど、 C++的にはやっぱ無駄なアロケーションとかでメモリ構成悪くして断片化って嫌だし。
て、いうわけで、初期化リストはconstなメンバの初期化とかみたいな必要最低限な場合以外で 使う場合は注意したほうがいいよな。って最近思った。
int main() { hash_map<const char *,int> map; char *p = new char[4]; strcpy( p, "abc" ); map[p] = 4; free(p); printf("%d\n",map["abc"]); return 0; }ってやると駄目っぽかったので、自分でメモリ管理しないといけないのかもしれない。 っていうか、これ何回か調べたことあるような気がするんだけどな…忘れる。
寒くなってきて、熱い茶が必要になってきたので、 「冷たいお茶が出る」という吉野屋のアドバンテージは小さくなった(松屋は熱い茶はあるけど、冷たいのは水しかない)。 +10円で味噌汁が付くと思えば松屋のほうが大分得だと思うんだけど、っていうか、最近松屋ばっか。
--
プログラマだったら一度は「自分言語を作ろう。」と思ったことがあるかもしれない。 でも、実際に実装なんか全然することなんかなくて、即没。っていう人が9割以上だと思う。 もし、実際に実装したとしても、生き残って広く使われるとなると、その確率は ごくごくわずかだと思う。(ちなみに、Xftのkeith氏も言語作ってたりする - http://nickle.org/。 全然関係無いけど。)
それでも、本気で自分言語の仕様について考えてみる価値はあると思う。
経験とかライブラリの知識とかの都合上、あるひとつの言語弄ってばっかり。 っていう状況は十分考えられるだろう。結果として、 その言語マンセーな状況、いわゆる「信者」になってしまうんだと思う。 大抵のプログラマは大なり小なり何らかの言語の信者だ。
でも、プログラマ、というか、技術者にとって、何かを盲信するというのは 最も避けるべき行為だ。そういうのは客観的に長所、短所を見極める際の障害になってしまう。
で、そういうのを避けるために自分言語を考えるのである。 自分言語を作るとすれば、もちろん、その言語を世界最高の言語にしようとするだろう。 そうすれば、自然と、他の言語の欠点を探そうとするんじゃないだろうか。 そして、その時は自分が信じる言語さえも客観的に見れると思う。
でも、そういううんちくはどうでもいいとして、 自分言語を考えるってゆーのは結構楽しい。 まあ、暇潰しとかにもってこいだ。
--
で、色々考えてて、結構考えがまとまってきたし、 今日は何か他の言語からパクってこようと思って、D言語調べてたら 僕が考えてたアイデアがもっとしっかりした形で十分実装されてたので なんか、もうD言語でいいや。とか思ったんだけど、続きます。
ねむー。
メッセージ投稿付けてみた。なんか面白かったら引用するかもしれないので、嫌な時は引用するな。 って書いておいてください
--
C言語は世界で最も広く知られ、使われているプログラミング言語だ。多分。 少なくとも書かれたコードの量では世界一だろう。
C言語以降、「俺の言語はC言語よりも素晴らしいYO!」な感じのモダンな言語は 数多くあるにもかかわらず、C言語がトップの座にいるのには何らかの理由があると思う。 その理由は何だろうか。
まあ、色々あると思うけど、僕は「ランタイムライブラリが不必要」 というところにあるんじゃないかと思う。 標準ライブラリだとかそういうのではなくて、 暗黙的なサブルーチン呼び出しが存在しないという意味だ。
今生き残っている言語の中でこの特徴を持っているのは Cぐらいだと思う(FORTRANはどうか知らないが、 あれは強力な言語とは呼べないので考えないことにする)。
「Cよりも強力な言語」の力っていうのは便利な 暗黙のルーチン呼び出しによる部分が大きいんじゃないだろうか。 まあ、そういうのは殆どの場合便利なんだけど、それで Cと比べて強力っていうのは、卑怯なんじゃないだろうか。スタートラインが違うというか、なんというか。 ランタイムライブラリ無しで得られる強さのギリギリのところをCは持っていると思う。
けど、Cは生まれてから長いこと経つので、いくつか古臭い部分を持っていることも確かだ。 特にプリプロセッサの中途半端さは改善すべきだろう。 マクロは言語を無茶苦茶にする能力は強力だけど、文法を強化する能力は弱いし、 #includeは機構が単純過ぎるし。
要するに、
「C言語の哲学を残しつつもモダンな感じを採り入れた言語を作る」
と、いうことだ。どうせ、完成しないと思うし、気楽にやっていこうと思う
続く
class Nanika { int x=0x208000; // 参照っぽい値 } new Nanika(); new Nanika(); System.gc();とかってやって、もし0x208000にオブジェクトがあったら解放されてしまう。