C++によるプログラミング入門
クラスを作ろう
こんにちは。いよいよ本番です。
今日の内容を、初心者が理屈だけで、はじめから理解するのは難しいと思います。しかし、プログラミングは「習うより慣れろ」ということばがあてはまる世界です。解説をじっくり読むより、さっと読んで、サンプルをコンパイル・実行してみてください。今日のポイントは「とにかくコンパイル・実行」です。うまく動いたら、ごく簡単な改造をしてみてください。そのうち、いろいろ、わかってくることと不思議に思えることが、あらためて出てくることでしょう。そうなってから、今度はもう少しゆっくり解説を読んでみてください。
文字列をいれる「いれもの」や、整数をいれる「いれもの」は
string name;
int tosi;
などと、作るのでした。このような「いれもの」を変数というのでしたね。このように「作る」ことを、業界用語では、「定義する」と言います。この言葉を使うと、上に書いてあることは、「nameとtosiを定義している」とも言えるのです。
これは、よく見ると(というほどでもありませんが)文字列を意味するstringや整数を意味するintがまずあって、次にその変数が書かれています。つまり、上のコード(注:プログラム全体やその一部分という意味で「コード」という言葉を使います)は
文字列のいれものを作るぞ。その名はnameだ。
整数のいれものを作るぞ。 その名はtosiだ。
などと読めます。C++ではいろいろな「もの」についてこの形を使います。この形はとても重要なので覚えておいてください。
さて、今、「もの」と書きましたが、どんな「もの」があるのでしょう。一覧表は一番下に名前だけ出しておきます(注:詳しくは教科書などを見てください。この講座を読み進めるとだんだん使い方もわかってくると思います。)が、このような「ものの種類」を一般に、型とよびます。つまり、ちょっと業界風に言うと、「nameはstring型の変数」「tosiはint型の変数」などと言えるのです。
逆に、変数の一般的な定義の仕方は、
型 変数;
ということになるわけです。(最初の例「string name;」などと見比べてみてください。くどいですが、stringが型で、nameが変数ですね。)
しかし、「文字列のいれもの」や「整数のいれもの」のほかにもっと別の種類の「もの」がほしくなったら、どうすればよいのでしょうか。実は、C++ではプログラマ(つまり私たち)が勝手に作ってよいのです。すばらしいと思いませんか。メモリの中は小さいながら一つの世界です。その世界に私たちが作った「もの」(=オブジェクトなどといいます)を置くことができるのです。コンピュータの達人をウイザード(魔法使い)などと呼びますよね。もとの意味は知りませんが、メモリの世界でいろいろな「もの」を創ったり消したりできるプログラマはまさにウイザードだなと、私は思います。
というわけで、例として、「猫」をつくることにします。もちろん本当の猫は無理ですが、とりあえず、次のようなデータと動作を持つことにします。
猫:
データ:名前
動作 :鳴く
これは、「猫そのもの」というより「猫の設計図」(「猫の型」といいます)です。これをC++では、たとえば、次のように書くことができるのです。ややこしそうですが、どんどん読み進めてください。
class Neko
{
private:
string name;
public:
Neko(string s){
name = s;
}
void naku(){
cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
}
};
はじめにも書きましたが、以下の説明は初心者にはそれほどすっきりしないかもしれません。なるべくわかりやすいように書いたつもりではあるのですが。いずれにせよ、はじめは、とりあえず、細部にこだわらず、ざっと読んでください。先に進むにつれわかってくると思います。ただ、そのためには自分でコンパイル・実行と、サンプルの改造をする必要があります。
さて、解説です。まず、「猫」というものを定義するぞと、宣言しなければなりません。それが第1行目の
class Neko
です。クラスとは自分が定義したい「もの」の設計図(=「オブジェクトの型」)です。ただし、「猫」という日本語は使えないので、対応するクラス名をNekoとしたのです。
そして、このクラスの定義は、すぐ下の「{」から、最後の「};」までです。
クラスは、 class クラス名 などと定義するものなのです(「;」を忘れないように注意してください)。 |
クラスNekoの定義もそうなっていますよね。面倒がらずに中カッコの位置を目で確認しておいてください。「見慣れる」ということはとてもとてもとても重要です。
「猫」は、名前を持つことにしました(「ボス」とか「たま」とか、個別の名前です)。今書いているのは、個別の「猫」ではなく、その設計図ですが、もちろん、設計図に「名前」の置き場所を書いておく必要があります。それが4行目の
string name:
です。これは、文字列の「いれもの」ですが、これをクラスの定義の中に書いたわけです。
前後にあるprivate:とpublic:ははじめの内はあまり気にしないでください。private:とあるのは、その下のものはそのクラスにプライベート(非公開)なもので他のクラスのものはさわれないという意味です。一方、public:の下にあるものは、そのクラスの外でも使える(つまり、公開)という意味になります。その意味は、次回以降に説明します。
そのpublic:の下には、
Neko(string s){
name = s;
}
というものがあります。これがひとつのカタマリです。「猫の設計図」には書きませんでしたが、これは、コンストラクタという重要なものです。
これは言わば「猫を創る関数」(あるいは「猫を創るための関数」)なのです。「何のことだ?」と思った方、あなたの感性は正常です。はじめは馴染みにくいかもしれませんが、ゆっくりやっていきましょう。
まず、C++では何かするもののことを一般に「関数」とよぶのです。関数は、一般には、外からデータを受け取って仕事をします。(ただし、すぐ下で見るように、データを受け取らない関数もあります。)私がここで書いたNekoは、関数の一種で、「文字列」を受け取って「猫」をつくるための関数なのです。
「あれ、Nekoというのはクラス名?関数名?」と思ったなら、するどい。そうです。これは、NekoというクラスにあるNekoという名前の関数なのです。このように、クラス名と同名の関数は、特別なものでコンストラクタとよばれるのです。(注:コンストラクタは「特別な関数」なので、普通の関数とは別に扱うこともあります。)
コンストラクタであるNekoの後の丸カッコ「(」と「)」の間にある「string s」は、「このコンストラクタ(クラスと同名の特別な関数)は文字列を外から受け取り、それをsとする」という意味です。そして、コンストラクタのすることは、その後の中カッコ「{」と「}」の間に書きます。今の場合、それは「name = s;」です。これは「外から受け取った文字列(sで表わされる)をnameに代入する」ということです。文字列は、=で代入できるのです。
これでクラスNekoのコンストラクタの説明は終わりです。しかし、その使い方を見なければ、何のことだかさっぱりわからないと思います。「外から文字列を受け取る」って何なんだろう、とか。これは、少し後で実例をお見せしますので、それまでちょっとシンボウしてください。たぶん、見れば「使い方」(「使われ方」)はすぐにわかると思います。それがわかりさえすればよいのです。
もう少しクラスの説明を続けましょう。コンストラクタの次には
void naku(){
cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
}
と書いてあります。これが、最初の「猫の設計図」で考えた「動作:鳴く」に相当する部分です。C++では何かするものを関数というのでした。これが、つまり「猫が鳴く関数」(見方を変えれば「猫を鳴かす関数」)なのです。
はじめにあるvoidは、この関数が「何も報告せずに終わる」という程度の意味なのですが、これも、初心者にはなんだかわかりませんよね。しばらくは気にしないで、単純な関数の頭にはvoidがつくんだな、くらいに思っておいてください。(コンストラクタの頭には何もつけません。これもそういう決まりなのです。)
そして、nakuがこの関数の名前です。その後の丸カッコ「(」と「)」の間が空なのは「データを外から何も受け取らない」ということです。コンストラクタは文字列を受け取るようにしましたが、nakuは何も受け取らないようにしたのです。このように、外からデータを受け取らない関数もあるわけです。
ずいぶん前置きが長くなりましたが、naku()のあとの「{」と「}」の間にある処理が、「この関数がすること」です。これは、つまり、「にゃあ。俺様はname(つまり、与えられた名)だ。」と画面に打ち出すということですね。(この猫は本当に声をだすようにはしていません。念のため。)
なお、「外から受け取るデータ」のことを業界では引数といいます。これからこの言葉も使いますので覚えておいてください。この言葉を使うと、「コンストラクタは引数を取る」「nakuは引数を取らない」などとなります。
どうもややこしいですね。でも、上のコードをもう一度見てください。書いてある文字数はほんのわずかです。はじめはこれを丸写しにして使ってみてください。
さて、クラスNekoができました。これは、最初に書いたように「猫の設計図」です。設計図というものは、普通は、それ自体では何にもなりません。「実体」ではないからです。一般には、設計図をもとに「実体」を作り、それを使うのです。
クラスから生成する「実体」を、業界では、インスタンスとかオブジェクトといいます。クラスNekoを書いたから、次は、そのオブジェクトを生成し、それを使うコードを書く必要があるわけです。そのオブジェクトは、
Neko dora("ボス");
のように生成することができます。ここでdoraがオブジェクトを意味するのです。(「string name;」や「int tosi;」に似ていますよね。基本的に、これと同じことなのです。)これで、「ボス」という名前を持ったdoraというNekoオブジェクトができたことになります。うーん。なんだか難しいですね。
これ以上言葉の説明を読むより、まず、実例を見た方がよいと思います。ということで、このクラスNekoを使った簡単な(あほな)プログラムを紹介します。そのあとで説明をしましょう。
//neko.cpp
#include <iostream>
#include <string>
using namespace std;
class Neko
{
private:
string name;
public:
Neko(string s){
name = s;
}
void naku(){
cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
}
};
int main()
{
Neko dora("ボス"); //「ボス」というの名前を持ったdoraが生成される
cout<<"あなたの名づけた猫がメモリ上に生成されました。"<<endl;
cout<<"猫が鳴きます。"<<endl;
dora.naku();
}
とりあえず、実行画面も先に見ておきましょう。ただし、さっと見たら、すぐ下の説明を読み、それからまた見直してください。
なお、この段階では、コンパイル時に「関数をインラインにできない」という意味の警告が出るかもしれません。警告が出てもエラーではないので、実行できます。もちろん、「警告」も重要なのですが、「インライン」については、次回の説明とさせてください。そして、そのときに説明するように、「関数の定義をクラス定義の外に書く」ようにすると、この警告を消すことができます。とりあえず、これは、次回まで気にしないでください。
Fig.1 neko.exeの実行。
プログラムを説明します。
はじめのほうにインクルード文とusing宣言があります。これは、C++プログラムの基本形でしたね。それからクラスNekoの定義があります。これは、上で説明したものと同じです。同じですから、今はあまり見ないで、mainを見てください。
1行目の
Neko dora("ボス");
は、上に書いたように、Nekoオブジェクトdoraの生成です。(これが本日のハイライトです!)
まじめに考える人は考え込むかもしれませんが、なるべく簡単に考えてください。これで、Nekoのオブジェクトdoraが生成されるのです。その際に、doraのnameには「ボス」が格納されます。
どういうことかと言うと、上のように書くと、
1.Nekoのオブジェクトが生成され、それがdoraで表わされるようになる。
2.その際、丸カッコの中に書いたデータ(今の例では「"ボス"」)が、
引数としてNekoのコンストラクタに渡され、そのコンストラクタが実行される
ということになっているのです。(「引数」とは「外から受け取るデータ」のことでした。「外から受け取る」とは、つまり、このようなことを言いたかったのです。)
ここで、「コンストラクタが実行される」とは、その定義に書いた内容が実行されるということです。コンストラクタでは、「外から受け取ったデータ」がsで現されるのでした。そして、そのコンストラクタにより、sに格納されている文字列(今は「ボス」)がdoraのnameに格納されるのです。
一般的に言い直すと、 クラス名 変数(引数); というコードを書くと、「この変数で表わされるオブジェクトが生成され、そのときにコンストラクタが実行される」ということです。 |
「Neko dora("ボス");」では、Nekoがクラス名、doraが変数で、「"ボス"」がコンストラクタに引数として渡されるのです。このコード以降は、doraがNekoオブジェクトを表わすことになります。)そういうものなのです。
ただし、引数を取らない場合、変数の後に丸カッコはつけなくてよいことになっています。(これは入門8で実例を見せますので、今は気にしないでください。)
Fig.2 オブジェクトの生成とコンストラクタ
初心者は、「どうしてFig.2のような形でコンストラクタが実行されるのだろう」と悩むことが多いようです。これは、「そう決められているから」なのです。ただ、「変数を見ないと、コンストラクタの形になっている」と気が付くと、「そういうものかな」と思えるかもしれません。つまり、「Neko dora("ボス")」で、変数を取り除いて縮めると、「Neko("ボス")」になります。これはまさに「Neko(string s)」というコンストラクタの形ですね。
なんにしても、(くどいですが、)「Neko dora("ボス");」の形は、「string name;」や「int tosi;」と(ほぼ)同じであることに注意しておいてください。そして、これはあくまでメモリ上の処理なので、その部分の出力はありません。
そのあとの2行は、出力の命令ですね。これは「あなたの名づけた猫がメモリ上に生成されました。」「猫が鳴きます。」と画面に表示しているだけです。
そして、最後の行は
dora.naku();
です。これは、Nekoであるdoraに、関数nakuを実行させているのです。(注:見方を少し変えて、「doraに対してnakuを実行している」という言い方もあります。)ここで「nakuを実行」というのは「nakuの定義に書いてある処理の実行」という意味です。その定義では、引数(=「外から受け取るデータ」)がないように書いてある(つまり、Nekoの定義中のnakuの後の丸カッコの中に何も書いていない)ので、「dora.naku();」の丸カッコの中にも何もいれていないのです。
これも一般的に言うと、 変数.関数(あれば引数); というコードを書くと、この変数(=「オブジェクト」を表わす)に対して、関数が実行されるということです。 |
「dora.naku();」では、doraが変数、nakuが関数で、その引数は何もないのです。
初心者には大変だと思いますが、もう一度、mainの中を見てください。要するに、これがクラスNekoのコンストラクタと関数nakuの使い方なのです。とても短いです。わかれば簡単なのです。
上の例では、Nekoとして「ボス」という名前を持つdoraを生成しましたが、ユーザが名前を決めることもできます。たとえば、neko.cppのmainを次のように書き換えることもできます。
int main()
{
string s;
cout << "どら猫を生成します。名前を入力してください。" << endl;
cin >> s;
Neko dora(s);
cout << "あなたの名づけた猫がメモリ上に生成されました。" << endl;
cout << "猫が鳴きます。" << endl;
dora.naku();
}
Fig.3 neko.cppのmainを上のように書き換えたもの(neko2.cppとする)を実行
たとえば、一番簡単な利用方法は、一度Nekoを書いておけば、
Neko dora1("ボス");
Neko dora2("タマ");
Neko dora3("トラ");
などと、簡単にたくさんの猫を生成できるのです。メモリ上に生成された猫たちのことを思いつつ、今回は幕としましょう。
今日の内容を、今日、すみずみまで理解できなければいけないということはありません。そんなことで苦しむより、上のサンプルを改造して「犬」をメモリ上につくってみてください。それができれば十分なのです。次回は今回の話の補足をします。それでは、また。
補足:主な基本的なデータの型
(boolを除く)データ型の範囲は、環境によって異なります。
以下のものは、現在の代表的な32ビット環境のものです。
種類 | 型名 | 標準的な範囲 |
真偽値 | bool | true false |
文字 | char | 英字数字など |
(符号付)整数 | int | -2147483648 ~ 2147483647 |
符号なし整数 | unsigned int | 0 ~ 4294967295 |
小数点数 | double | 1.7 x 10の-308乗 ~ 1.7 x 10の308乗 |