さて、今回がポインタ編一応の最終回です。「ポインタの具体的な使い方」は他の良書を参考にしてもらうとして、今回は「ポインタと参照をどう両立していけばいいのか」ということに注目してみましょう。
|
ポインタ派VS参照派
とかいうものが実際に存在するかどうかは未確認だけど、でもじっさいに「ポインタは使いたくないな」「参照は使っちゃダメでしょ」みたいな意見を持ってる人は結構いるみたいです。要約すれば「見やすさや打ち込みやすさを考えてどちらかに統一したいけど、実際にはどちらも使わざるを得ない」ということなのでしょう。 バグを減らすコツとして「いろいろな方法がある場合にはひとつに統一する」というものがあります。参照とポインタを混ぜて使ってもそれほど危険ではありませんが、明らかに可読性は落ちますし、コーディング時のミスも増えるでしょう。 そのため、実際には統一したい、でもMFCの中では混ざって使われているし、部下のコードでは参照がよく使われている、できれば両方のいい部分を使いたい、そういう現実が「結局どちらも使う」という選択になっているのでしょう。
ポインタはシステムの根底に関わっていて、フレキシブルな機能を持っています。つまりポインタは、これひとつあればたいがいのことは事足りるだけのポテンシャルを持っているから、とても便利だということになります。ですが、その分使いこなすには深い部分まで理解する必要があって、それが「初心者キラー」とまで呼ばれ、また上級者にとっても、ちょっと危なっかしい代物というイメージを持たせています。
これを、簡単なたとえ話に置き換えてみましょう。 |
ナイフとカッター
ちょっと物騒かもしれませんが、ポインタを「ナイフ」に、参照を「カッター」に置き換えてみましょう。
ナイフはとても便利な道具です。「ツタを切る」、「木を削る」、「毒虫を刺す」、「土を掘る」など、さまざまな使い方ができます。ジャングルのようなサバイバルな環境では、ナイフひとつあるだけで様々なことができるようになります。
と、ここで場所をオフィスに変えてみましょう。ナイフを使う機会はあっという間になくなります。使えるとしたら紙を切るくらいでしょう。
さて、ここでまた場所をジャングルへと戻してみましょう。ここで手に持っているのがカッターだったら、どうしましょう。カッターは「歯を折る」ことで切れ味を保つため、当然刃がすごく折れやすくなっています。木を削ったり土を掘ったりしたらすぐダメになってしまうでしょう。それに先が尖っていないので危険な虫を刺し殺したりもできません。つまりカッターだけ持っていてもジャングルでは生き残れないのです。 |
WindowsとMFC
では話をプログラミングに戻しましょう。現実では「ジャングル」と「オフィス」がいきなり入れ替わったりしません。でもプログラミングではこのふたつが隣り合わせに存在しています。つまり、「オフィス」は「ジャングル」の中に作られている、ということです。そしてしばしば、オフィスからジャングルへと出なければならないのです。 もうおわかりだと思います。ここでの「ジャングル」は何もない環境、Win32SDKのみのプログラミング環境で、「オフィス」はいろいろなものが揃った環境、MFCやATLに守られた環境です。
普通にプログラミングをしている場合、そこは環境の整った「オフィス」でしょう。MFCやiostreamのようなクラスライブラリ、ATLやSTLのようなテンプレートライブラリ、そのほか各種コンポーネントが揃い、至れり尽くせりの世界です。こういった環境では参照のような、特定の機能に特化されている安全なものが奨励されます。
そう、あなたはジャングルへと赴かなければならないのです! ジャングルへと出て、自分の力で新しいオフィスを作ったり作り直したりしなければならなくなるのです。APIやCOMインターフェイス、各種アルゴリズムを使いやすくするための関数やクラスを作る必要が出てくるわけです。
このとき、あなたに奇妙な誘惑が生まれることでしょう。そう、「そのままここで仕事をしていいんじゃないか」というものです。たとえジャングルの中ででも、泥まみれでも、ちゃんと仕事はできます。ナイフがあればカッターは要りませんし、それ以外の様々なこともできます。 |
オフィスとジャングルでの分業
さて、実際にみなさんはおそらく、オフィスとジャングルを行ったり来たりしていることでしょう。でも今見てきたように、その間にはちゃんとした「壁」が存在します。再利用化を考えたクラスやコンポーネントがオフィスの便利な道具となることで、ジャングルに行く必要性をなくせるわけです。 ここに、参照とポインタを混ぜ合わせない秘訣があるのではないかと思います。オフィス向けの道具はオフィスの中で使い、ジャングル向けの道具はジャングルの中で使えばいいのです。つまり「棲み分け」です。
ポインタや配列を駆使したアルゴリズムや、ShellAPIやCOMAPIの操作などの「ポインタ必須」のものは、関数やクラス、コンポーネントの中に封じ込めてしまいましょう。そして、これらを使う時にはポインタを使わず参照を使うようにすれば、簡単に「棲み分け」することができます。
実際、こういったことはすでに行われているわけです。クラスや関数のパッケージングはこういうことのためにも重要に機能します。Cランタイムライブラリは、さらに細かい作業をしていますが、それを関数という形に封じ込めて、簡単に操作できるようにしています。 |
MFCと参照
ところが、ここで問題が出てきます。それはMFCです。MFCは参照とポインタをぐちゃぐちゃに混ぜて使っているため、とても混乱します。 まず参照ですが、MFCではポインタよりも使われていない傾向にあります。一番使われているものはCStringを引数に取るもので、これはCStringが「文字列を受け取ること」が苦手なためです。 でも、CStringを含めて参照を引数に取るメンバ関数は、たいがいポインタを使ったメンバ関数がオーバーロードされているので、参照を使いたくない場合はそちらを使う方がいいでしょう。
ただ、一部のメンバ関数の中には参照を使ったものしかないものもあります。こういったものを使う場合には、参照を使わざるを得ないでしょう。ただ、引数として渡すだけなのでそれほど混乱はしないと思います。 |
MFCとポインタ
前述したように、MFCではポインタがメインです。そのため、ポインタを使わずに済ませるというのは不可能でしょう。特にクラスのやりとりにポインタを使用しています。MFCは「APIのラッパークラス」としての意味合いが強く、ハンドルそのものも一種のポインタということもあって、その方が自然と判断されたのかもしれません。 中にはCWnd::CreateControl()ように参照とポインタをごっちゃに使っているものもあります。また、読み込み用のポインタなのにconstを使っていないものも多く(もしかしたら裏で処理をしているのかもしれません)、仕様としてはちょっと不安な所も多く見受けられます。でも、やっぱりMFCを使う上で、ポインタは不可欠でしょう。
現実問題として、MFCは「便利なクラスライブラリ」としよりも「APIのラッパークラス」としての部分があまりにも強すぎるのかもしれません。けれども、難しいウィンドウズプログラミングを楽にこなすためのクラスライブラリ、としての側面もあって、そのために仕様が混乱しているのかもしれません。 |
参照から解説したワケ
さて、最後になりますが、この「ポインタ編」の趣旨、みたいなものを書いておこうと思います。
「Codian」は、Visual C++ユーザーのための講座です。それをふまえ、VCユーザーにとってポインタを一番理解しやすいであろうと考えて書いたのが、この「ポインタ編」です。
MFCでは「引数に*が付いていたら&を付けて渡せばいい」くらいの知識で十分な領域です。ポインタのインクリメントとか、ホントは整数値なんだとか、そういったことは知らずにいてもそれほど問題なく、また問題の出るコードを書く機会もない、そういう環境です。
ところが同時に、VCはある意味「もっともローレベルでのコントロールを行える唯一のツール」でもあります。VBのような他の開発環境では作成できないものを作り、操作できないものを操作する、それがVCの役目でもあるのです。
VCを使い始め、勉強していって初めて当たる壁は「ここ」にあると思います。これはポインタだけではなく、MFCとSDKの違い、VCが覆い隠してきたウィンドウズの中身など、そういった「わけのわからないもの」に触れる恐怖が、ここにあります。
こういった趣旨があるため、たとえば「UNIXでCを学ぶ」という方にはおそらくあまりお役には立てなかったと思います。けれども、「VCからC言語を学び始めた」という方には、ポインタを理解する上では結構有益なんじゃないかなぁと思います。
と、最後におまけ。次のページに「ポインタQ&A」があるのでそちらもどうぞ。 |
(C)KAB-studio 1998 ALL RIGHTS RESERVED. |