Qtを始めよう(スキルアップのためのプログラミング講座)第2回 Windows編
こんにちは!今日も少しずつプログラミング学習です。
ところで、第1回の内容、皆さんついてこれましたか?
どんなものでも波に乗るまでは難しいのです。
今回は、前回のソースコードをちょっと分析してみたいと思います。今回は難しいですよ。「Qt Creator」を立ち上げてください。
Open Project
過去に自分が作ったファイルを再び呼び出します。今回は「Create Project」ではなく、「Open Project」をクリックします。
すると、ファイルダイアログが開くはず。昨日設定したパスを通ったところにあるファイルが開くはずです。Sample Project と言う名前のファイル。ありますか~?
ファイルダイアログは、上の方にパスの通り道が書いてあります。ここにパスを直接書き込むことでそのファイルへ飛ぶこともできるんです。
これは、C:\Qt\src_self\SampleProject
と同じ意味ですね。
\は日本語のOSだと¥マークになることもしばしば。
検索エンジンは優秀なので、ファイル名をちょっと打ってReturnキーを押しても検索してくれますよ!でも、範囲が絞られているに越したことはありません。
とりあえず、Sample Projectのファイルを開くことができたら、中身は今こうなっていました。
これで、CMakeLists.txt(一番上のやつ)をダブルクリック。コックを召喚します。コックは厨房のことを何でも知っているので、下にあるファイルを脳内の記憶から取り出してくれます。
このように、複数のファイルが一つにまとまって、初めてソフトウェアが完成するわけですが、ソフトウェアになる前の、1つのフォルダの下に集まったファイル群をまとめて「プロジェクト」と呼んでいるんですね。
Ctrl+K ショートカットでファイル選択
さて、第1回でお話しました。QtCreatorは、マウスで操作するよりも、キーボードで操作をする方が圧倒的に楽です。この際ですから、キーボード操作を覚えてしまいましょう。
昨日は「F4」を学びましたね。これは、ヘッダファイル(.h)とソースファイル(.cpp)の切り替えを行うことができるものでした。
今日学ぶのは、「Ctrl」を押しながら「K」を押すショートカットです。早速押してみましょう!
はい。このような窓が出てきたら成功です。これは、「ファイル検索」ダイアログです。いきなりですが、「Main…」と出だしの文字を打ってみましょう。
ここで、候補が絞られますから、自分の開きたいファイルが見つかったら、方向キー(上下)を使ってそのファイルを選択し、「Enterキー」を押してみましょう。ここでは「main.cpp」を選択してみましょうか。
プログラミング学習者が、コードとして最初に学ぶのは、おそらくこのmainという関数や、それと同類のもののはずです。なぜならば、main関数が、「最初に実行される関数」だからです。
でも待てよ?第1回で、プログラムは左から右に、そして上から下へ読み込む。じゃあ、mainよりも先に#includeが読み込まれるはずじゃないか。あなたそう言ったじゃないですか?嘘つきだ。ペテン師のような名前と経歴してると思ったら。ということになりそうです・・・。ところが、そうではありません。ちゃんと#includeから読み込まれます。
#include
これは、別のファイルを読み込む と言う意味です。第1回で「クラスの設定」を行った時、さりげなく「MainWindow.h」「MainWindow.cpp」の2つのファイルを作りました。これは、結局はこういうことです。
そして、<QApplication>のファイルが読み込まれる。
Qtファイルで、「QApplication」と検索してみると、このようなファイルが出てきます。結局これもファイルなのです。Qtは料理をするための調理道具だ、と言いましたね。まず、道具をそろえてからレシピを見て料理を始めるのです。MainWindowファイルと、QApplicationファイルは、どちらも調理を行うために必要な最初の道具なのです。特にQApplicationは、厨房そのものと言っていいくらい大事。滅茶苦茶でかいファイルなので載せません。
私たちは自分達で自分達の道具を作ることもできます。今回もさりげなくそうしました。"MainWindow.h"と"MainWindow.cpp"は、Qtにはそもそも存在していなかったファイルです。
このようなファイルは、#include "MainWindow.h"と「"」を使ってダブルクオーテーションでファイル名をくくります。
一方、Qt由来のファイルは、#include <QApplication>と<>でくくるのです。(と言っても、"qapplication.h"でもOKみたいなんですよね・・・。おそらく、Qt自体も他の人が作ったものであり、C++そのもののファイルではないからだと思います。)
で、includeの順番は、「私たちが作ったファイル」⇒「Qtにある元々のファイル」という順番がいいようです。
とりあえず、今はあまり深く理解しなくていいです。#include ファイル名で、そのファイルの中に書かれているコードが読み込まれているんだな・・・。それだけわかればよいです。
何はともあれ、そこからようやく「main関数」が呼び出されることになります。「main関数」は、「プログラムの最初に呼び出される関数である」、というだけで、何も「最初に処理されるコードではない」のでご注意ください。
そしてようやくmain関数にたどり着きます。ここで、プログラミング初心者からすると謎の文句がずらりと並んでいますね。
4行目をざっと見ると、
int : 関数の戻り値の型
main : 関数の名前
(int argc, char *argv[]) :引数 コール演算子と引数の型、引数名 ポインタ演算子 配列演算子
・・・(5行目以下へ行くと)
{内容省略}:関数の中身
return :関数の終了を宣言し、値を返す
4行目全部をいきなり理解しなくてもいいです。どんな場合でも、ここをいきなりいじってプログラムを学んでいきましょうという話にはならないんじゃないかと。私自身、あまり使ったことがないのです。それよりは、皆さんの身近な所に話を落として考えましょう。
関数
皆さんは、中学生になると、「関数」を学びました。そして高校生になると、
y = x + 1が f(x) = x + 1みたいになったのを覚えていませんか?
これもそれと同じで、main(int argc, char *argc[]) = {内容省略}
みたいになっているのだととりあえずは考えてみてください。
学生時代の「関数」の勉強は、実は根本的に間違っているのです。関数は、本来中身が見えない式なのです。だから、「ブラックボックス」と呼ばれるのです。xに数値をいれたら、結果としてyの値が求まる。この関係にあることです。
頭がよかったはずのあなたたちの頭は、ここでかなり苦しんだと思います。
「モロ見えやんけ・・・。」
そうなんです。関数の「作り手」と言うのがいるのです。どんだけ中身の見えないものでも、必ず作り手がいるはずです。今回の場合、それが私たちプログラマなのです。「作り手」にとって、関数は丸見えですし、内部構造がどうなっているのかもはっきりとわかっています。そして、次はこういう突っ込みがくるのではないでしょうか。
「でも、main関数のことなんて、私たち全く知りませんけど・・・。」
そりゃ、他人が書いたプログラムだからですね。他人もプログラマです。皆さんはまだ初心者のはずですが、プログラマになりました。だから、関数の中身とは切っても切り離せない関係ができたのです。他人の書いたプログラムは、関数の中身が隠蔽され、たださりげなくコールされていく、そういったコードが内部にちりばめられています。私たちは、そのコードを理解したくなる時があります。そうしたとき、中身を一生懸命探します。
今回はいきなり、main関数の中身が書いてあるところに出くわした。そういうことになります。{}の中身が、関数の内容であり、値です。変数名と内容、値が全て書かれた状態を「定義」と言います。定義はソースファイル(.cpp)に書くのが通常です。
やがてコードは完成した「レシピ」になります。そのレシピを読んだ時、メイン関数は、このようにして呼び出されているはずです。
main(引数省略); (あるいは、int var = main(引数省略);)など。
4行目のように、仰々しく中身まで書いていません。ただ、ぽつりとこう書いてあります。関数が実行されるときは、ただこれだけなのです。
本来、関数は私たちが自然現象を観察し、変動するものを捉えることが目的で開発されたのです。例えば軍事。どの角度で大砲を打てば、相手の船に当たるか。何秒で砲弾が届くか。x:1秒、2秒、3秒、4秒と時間が動くごとに、y:100m, 200m, 300m 400mという結果として返される。この値が返ってくるまでの間に、細かい計算が行われているわけです。1秒をxに当てはめて、後は計算するだけ。その計算過程はあらかじめ整えられている。それが関数です。コンピュータがある場合は自動で計算されていましたが、ない時代は、あらかじめ整えられた式に当てはめて計算をしていったわけですね。また、いちいちそれだと面倒だということで、表などを作り、xのときのyの値が瞬時に出せるようにしていた、ということもあるでしょう。
つまり、本来あるべき関数の勉強とは、「自分達で自然現象を観察し、変数値を掴み、その値が出てくるような関係を式に落とし込む訓練」です。
決して、「どこかの誰かが考えてきた関数の内容を見て、xに指示された数値を入れ、後は今まで習ってきた計算方法を駆使して、yの値を出すこと」、ではありません。
プログラマは、毎日のように本来の作業を行っています。
この時、f(x)のxを引数といいます。argc, argvは引数です。私たちが学んできたこととは違って、プログラムは引数をいくつでも作ることができます。
変数
argcとか、argvという名前は、変数名といいます。この場合、引数名ともいいます。例えば、f(x) = x + 1では、xは変数でもあり、引数でもありますね。
xが変化すると、f(x) つまり yの値も変化する。
プログラミングでは、この知識よりも、もっと大切なことがあります。それは、「値を保持する」という役割です。
変数は、プログラミングでは、「箱」にたとえられます。xが1であると仮定すると、xという「箱」に1という数字がかかれた「ボール」がころりと入る。
どうしてこんなことをしなくちゃいけないんだろうと思いますか?私たちは、f(x)に1と入れたら、そのまま1+1=2で、ハイ終了!ってなるのに。いちいち1のボールを箱の中に入れてからやるとか、滅茶苦茶無駄な動きでしょ・・・。
これはあくまで例えです。xというのは、メモリ「記憶領域」を表します。
例えば、私たちがf(x)に1を入れるとき、1と言う数値をまず覚えていないといけません。f(x)に1を入れる瞬間、1という数字を忘れてしまったらだめなのです。何を入れるんだっけ?となります。
コンピュータは、私たちが当然できる事が出来ません。意外ですね。AIもあれだけ賢そうに見えるのに、実は人間よりも圧倒的に融通が利かない存在なのです。
でも、1という数字を忘れたらわたしたちだってあてずっぽうで数字を入れなければいけませんよね?私たちは、自然とそれができてしまっているんですね。あまりにも当然のことなので、その作業が見落とされていただけなのです。
つまり、f(x)を計算するには、1を入れる⇒「1という数字を入れるんだということを、私たちは記憶する」⇒1を入れて計算をする⇒そしたら2という数字が出てきた!という過程を経る、ということです。
変数の型
さて、intとか、charとかありますが、これは一体何なのでしょうか?
詳しいことはプログラミング本に譲りたいと思います。簡単に解説します。
コードは、まずメモリに割り当てられます。ビット、バイト、キロバイト、メガバイト、ギガバイト、テラバイトとかありますね。携帯電話でもギガ―!とか言ってますよね。
先ほど、1と言う数値をまず覚えないと使えない。これはコンピュータでも人間でも同じことなのでした。型は、私たちがもっと無意識で行っていることです。
例えば、私たちの記憶力には限界があります。はっきりとどれだけ覚えられるかはわかりません。たくさんのことを記憶できますが、限度があります。例えば、10000メモリパワーを持っている人間がいるとしましょう。そして、1という数字をいれるんだ!という記憶は、このうちの4メモリパワーを消費して初めて記憶できるということになります。私たちに残されたメモリパワーは9996メモリパワーです。
コンピュータもこれと同じルールを持っています。どれだけのものを記憶できるのかはあらかじめ定められています。intはinteger(整数)の略です。つまり、int argcと書かれているところには、0とか、1とか、-1とか、そういった数値が入ることが「予定」されているのですね。
仮にmain(4)と入れると、コンピュータは一旦、argcと書かれた箱に4と言うボールを入れます。これで記憶完了。そうしたうえで、{}の中身を計算します。(でも、argvもargcもどちらも関数内では使われていないんですよね。こういう関数も作れちゃうのです。)
型は、「いくらのメモリ領域をつかうか」ということをあらかじめ定めているということです。このような手法をとる言語を「静的型付言語」と言います。
例えば、4と言う数値であれば、私が書いた上の箱に収まります。しかし、329874932874832479247927382478297492478243274294783248274824327492478923729835297592752894とか、かなり適当に書いたこのとても大きな数値は、int型の箱には収まらないのはよくわかりますね?
コンピュータは、記憶力や演算力という点で、人間よりも圧倒的に勝っています。しかし、あらかじめ、適度な箱を用意してあげておかないといけません。(一応、型推定みたいなことを行ってくれるようにはなっていますから、自動で型が変換されることもあります。)
え・・・。その大きさってどうやってわかるの!?って初めのうちは思われると思いますが、慣れればなんてことはなくなります。そんなに数は多くありません。
今はとりあえず、変数と型の関係を理解してください。
型は、変数を入れる箱の容量です。変数は箱です。この箱に入ったものを見て、計算をしていきます。
戻り値
さて、int mainと書かれていました。このintは、戻り値の型と言いました。戻り値というのは、return の後に書かれた変数を見ます。
なんじゃこりゃ!と思いますが、ご安心ください。これも、関数なのです。exec関数です。(aは今、気にしないでください。)ということは、この関数も戻り値を返してくるはずです。端折って言えば、プログラムが正常終了した場合、この関数は1を返します。
1です。つまりint型に収まりますね。整数だし!
じゃあ、QApplicationとかMainWindowっていうのも型なの?って思いますが、そうなのです。最初見た時は、こんなのが型名なんて面白いな・・・っとか、人によっては奇妙に思われますよね。
とりあえず、今はこう考えてください。MainWindow.hやQApplicationがincludeして読み込まれた時、このファイルに書かれている変数値全部の合計(正確にはclass MainWindow内の・・・)を箱の容量としてとらえたのだと。だから、QApplicationという(ここは、関数ではなく、クラスのインスタンス化と言われるものなのですが、ここに限っては関数と同じように考えても問題ありません。)関数をコールしたとき、その戻り値が、QApplication型の箱におさまるようになったのである・・・。なんとなく出来レースな感じはしますが、とりあえずこのように理解しておいてください。型と聞くと難しく聞こえますが、箱の容量を定めた規格だということです。
4っていえばint型ってわかるっしょ?というのは人間の考えです。
ちなみに、short型っていうのもあったりしますし、これでもOKです。
1はbool型でも収まります。
私たちは、ある物事を覚えようとするとき、自分達が知らないうちに、その物事を覚えるのに必要な量を把握できているのですね。人間は意外と頭がいいのです。4と言う数値を覚えるだけで、脳のパワー全体を使ってしまう・・・。何かが狂うとそんな人も出てくるかもしれません。
ローカル変数とグローバル変数
変数にはローカル変数とグローバル変数に大きく分けられることがあります。ローカルは「地方の」と言う意味で、グローバルは「全体の、全世界の」と言った意味があります。
どちらも変数であることは一緒です。そして、箱の中に何らかの情報を持っているという点でも共通しています。
どこに違いがあるのかというと、「変数の記憶期間」です。
私たちは、f(x) = x + 1という関数を計算するとき、x=1として計算をする。そのとき、xは今1なんだ!と記憶しました。そして、計算結果として2が出てきました。じゃあ、もうこのxは1というのは覚えていなくてもよくなりますよね?というわけで、いつまでも1というのを覚えておくのはやめて、すぐに忘れてしまいます。
この事情はプログラムでもおなじです。関数内で作られた変数は、関数終了後にはメモリから捨てられるのが通常です。ローカル変数は、関数の中身の計算のためだけに使われる一時的なもの、という理解でいいでしょう。
こうすることで、9996パワーあったのが、10000パワーに復帰されるのです。
これに対して、グローバル変数は、「プログラム実行中は常時覚えておく変数」と言う意味になります。
グローバル変数は、どんな関数内でも呼び出される可能性がありますから、その関数の計算が終わればもう忘れていい、と言うことにはなりません。
グローバル変数は、そういう特徴を持ちますから、最初に値が定められた以上、プログラムの終了まで、値が変化しないことが期待されています。
プログラムの性質上、書き替えようと思えば書き換えることは可能です。しかし、それは推奨されていません。現実的には、プログラマがどこで書き換えるかわかったもんじゃないので、グローバル変数自体を作ることを禁止する、と言うことが多いようです。
まとめ
かなり我流で説明してきましたが、わかっていただけたでしょうか。
いきなりQtを学ぶ。C++の基礎もわかっていないのに・・・。でも、こうして知識の布石を置いておくことは重要です。いつか頭の中でつながるのではないでしょうか。
もちろんここで語りつくせなかったことは非常に多いです。しかし、それでもかなりボリュームがあったように思います。一気に詰め込まず、じわじわと行きましょう!まとまってない気がしますが、ではまた!