Version 7.07
構造体とクラス
「今回は、前回ごまかしちゃった構造体とクラスの関係について」
『難しいとか面倒とか言ってたよね』
「うん、なんて言うか、ややこしい話になるんだよね。まずずばり言うと、
クラスと構造体はほとんど違いはありません」
『ってことは、構造体をクラスと同じように使えるって事?』
「そゆこと。たとえば AnimeDlg.h の」
class CAnimeDlg : public CDialog
{
// 以下略。
「これを」
struct CAnimeDlg : public CDialog
{
「って変えてビルドしてみて」
『ほいビルド。あ、通った』
「というわけで、構造体はクラスと同じ。違いはたった一点、些細なことだ
からこれはあとでね」
『うんあとで。それより、ならなんで構造体なんてあるの? それに、この
前構造体は〈メンバ関数のないクラス〉だって言ってたし』
「そう、その辺からが複雑な話になるんだよね。ここで重要なポイントにな
るのは〈歴史〉」
『れきしぃ?』
「そう、歴史。今 VC で使ってる C++ 言語っていうのは、 C 言語っていう
プログラミング言語を拡張したものだって前に言ったっけ」
『ぽろぽろっと』
「で、構造体は C 言語時代からあったもの、クラスは C++ 言語で加わった
もの」
『クラスは新しいものなのね。あれ? でも構造体とクラスって同じ物なん
でしょ?』
「そこがポイント。 C 言語時代の構造体と、 C++ の構造体は、実は機能が
だいぶ違うんです」
『同じ〈構造体〉なのに?』
「そう。 C++ 言語になったときに、構造体の機能が〈クラスとほとんど同
じ〉になったんです」
『ってことは、 C 言語の構造体は、クラスみたいな機能はない?』
「そゆこと。それが、前に言った〈メンバ関数のないクラス〉なわけ」
『なるほど。構造体は、 C++ になってメンバ関数って機能が加わったわけ
だ』
「実際にはもっともっと多くの機能が加わったんだけど、それはクラスの機
能をまず知らないといけないから」
『また今度?』
「そういうこと。で、もう少し歴史のお勉強」
『続きがある?』
「うん。まず、 API 。ウィンドウズももちろん歴史があって、多くの API
も同じく昔からあります」
『そりゃ、 API がなけりゃウィンドウズ操作できないんだもんね』
「で、そういう昔には C++ がなかったから」
『 C 言語で作ってた?』
「だから API にはクラスがないでしょ」
『そういえばそうだね、クラスがあるのって MFC の方だもんね』
「そう、 API の不便な部分をクラスの機能で補おうっていうのが MFC の基
本的な趣旨。ちなみに MFC は Microsoft Fundation Class library の略」
『クラスって入ってるね』
「それが売りだったからね。 API はすべて C 言語向けに作られてるから、
構造体も」
『メンバ関数とかないわけだ』
「前回 RECT を見てもらったけど、 typedef とか使ってたでしょ。あれも
古い C 言語形式の書き方」
『不便ねー』
「ちょっと前までは C++ 言語ってそれほどメジャーじゃなかったから
ね。だけど、クラスじゃないと不便なのは違いないわけで、実は MFC に
CRect っていうクラスが用意されてます」
『便利なためにってわけね』
void CAnimeDlg::OnBDraw()
{
CRect cRect;
::GetWindowRect( GetSafeHwnd(), &cRect );
TRACE
( "%d, %d, %d, %d\n"
, cRect.left, cRect.top
, cRect.right, cRect.bottom );
}
『……別に全然便利になってないじゃん』
「これはね。これは前回の RECT 使った時の例と同じ事が CRect でもでき
るってことの確認のために」
『そういえば、まったく同じに使えるんだね』
「そう、それもそのはず、 CRect は RECT から継承してるんです。
AFXWIN.H の中を見てみると」
( AFXWIN.H より抜粋)
class CRect : public tagRECT
(抜粋ここまで)
「継承って憶えてる?」
『あるクラスの機能をまるまる使えるクラスを新しく作るんだよね』
「 Ver 3.1 ( No.026 ) でやったね」
『この CRect も tagRECT から継承してるから、 RECT と同じに使えるわけ
ね。おおっと言わなくていいよ、 tagRECT と RECT が同じってことは前回
やったのだよね』
「そうそう、 RECT は tagRECT から typedef されてるからね」
『……だからー、 CRect が RECT と同じに使えることは分かったけど、便
利って部分は?』
「そうだねー、たとえば」
void CAnimeDlg::OnBDraw()
{
CRect cRect;
::GetWindowRect( GetSafeHwnd(), &cRect );
TRACE
( "%d, %d\n"
, cRect.Width(), cRect.Height() );
}
「 CRect には CRect::Width() と CRect::Height() ってメンバ関数があっ
て、これを使えば横幅と高さが一発で出るから」
『……でもこれって、 right - left と bottom - top だよね』
「……あんまり便利じゃない?」
『少しだけ便利。あくまで少しだけ』
「じゃあそうだねぇ…… IDC_S_CANVAS の中央を四角く塗り潰してみましょ
う」
void CAnimeDlg::OnBDraw()
{
// IDC_S_CANVAS の左上隅と右下隅を取得します。
CRect cRect;
::GetClientRect( m_cCanvasStatic.GetSafeHwnd(), &cRect );
// サイズを縮小します。
cRect.DeflateRect
( cRect.Width() / 4
, cRect.Height() / 4 );
// ブラシとデバイスコンテキストを準備します。
HBRUSH hBrush = ::CreateSolidBrush( RGB( 0, 0, 255 ) );
HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
// 四角形に塗り潰します。
::FillRect
( hDC
, &cRect
, hBrush );
// ブラシを削除します。
::DeleteObject( hBrush );
}
『うわ長!』
「……長いってだけで拒絶するのは悪いクセだよ?」
『べ、別に拒絶してるわけじゃないもん。えーっと……あ、よく見ると見た
ことあるのばっかだね』
「もちろん」
『最初の CRect とかのは今回のでしょ」
「これから描画する IDC_S_CANVAS のってことに注意してね。それと
GetClientRect() ってとこも」
『 GetWindowRect() だとダメ? IDC_S_CANVAS ってタイトルバーとかな
いからクライアントとウィンドウって同じじゃないの?』
「同じだけど、 GetWindowRect() だと画面の左上からの座標が入っちゃう
から面倒なんだよね。 GetClientRect() は」
『サイズだけなんだもんね。これに描くんだったら、 GetClientRect() の
方がいいわけだ。次の DeflateRect() ってのが初見だね』
「 CRect::DeflateRect() は、中心方向に縮小するメンバ関数。たとえば」
cRect.DeflateRect( 10, 10 );
「ってすれば、上から 10 、左から 10 、右から 10 、下から 10 内側に狭
まります。あ、もちろん cRect の中の top とかがね」
『その引数に、 cRect.Width() / 4 ってことは、横幅の4分の1とかね』
「たとえば DeflateRect( 10, 10 ) した場合、左から 10 、右から 10 だ
から、横幅は 20 狭くなるわけ」
『ってことは横幅は、これまでの横幅の4分の1の2倍、つまり横幅の半分
を引いたのになるってことは……あー、結局半分になるわけね』
「そゆこと。そうひとつずつ考えてくとちょっとややこしいね……」
『その次はブラシ作ってデバイスコンテキスト取得してるね。これは
Ver 7.05 ( No.125 ) とかでのだからいいとして、次の FillRect() も初め
て見るの……だけど、これが四角く塗り潰す API ?』
「そう、第1引数のデバイスコンテキストの、第2引数の四角形を、第3引
数のブラシで塗り潰します」
『あれ? SelectObject() しなくていいの?』
「これはしなくていい API 。こういうのって API によって結構違うんだよ
ね、そういうのもこつこつ憶えていったり、憶えなくても〈色々ある〉って
ことに慣れておいて」
『そのためのデバイスコンテキスト編なんだもんね。んで、最後にブラシを
削除っと』
「うん、これまでの繰り返しがほとんどだから難しくはないと思うけど」
『質問!』
「はい火美ちゃん」
『 CRect は MFC のなんだから、他のも API じゃなくて MFC のを使うべき
なんじゃないんですかー?』
「だね。 FillRect() も CDC のメンバ関数として用意されてるし。でも、
もう API から MFC に乗り換えたり、ってできるでしょ?」
『そりゃ、ほとんど違わないから』
「だったら大丈夫。 API と MFC で違う時は両方とも解説載せるから」
『これは違わないからいいわけね。もひとつ質問』
「はい火美ちゃん」
『……これってホントに CRect の便利なとこ?』
「……さて最後のまとめ」
『うわ逃げやがった』
「もう一度構造体の話に戻るね。今はまだいいけど、最終的には古い C 言
語形式でも構造体を作れないとダメ」
『どゆこと?』
「普通に VC 使う時にはあり得ないけど、場合によっては古い C 言語形式
でプログラムを書かなきゃいけないときもあるから」
『…… VC 使わない時なんてあるの?』
「ってゆーかそういうのもいつか教えるから」
『げ、そなの? っつーかそれってこの講座の趣旨から外れてない?』
「いろんな開発環境を知ってこそのプロの VC 使い。実際、 VC マスターっ
ていうのは、プログラミングに関しては〈なんでもできる〉って思われるも
の」
『それに応えられなきゃダメってわけねー』
「ま、あくまでまだ先の話だけどね」
/*
Preview Next Story!
*/
『……なんか、憶えたことは少ないのに、難しく感じるなー』
「範囲を広げたからね」
『範囲?』
「歴史とか、 C 言語と C++ 言語の違いとか」
『そういうのもやんなきゃいけない?』
「 VC じゃね。憶えなくていい言語もあるんだけど」
『たとえば Java とか?』
「というわけで次回」
< Version 7.08 コンストラクタで初期化 >
『につづく!』
「ま、 VC の道は険しいってことで」
『ゴールが見えないっていうのは辛いのよね……』