メインコンテンツに移動

Hello, Class

クラスとかオブジェクトとか

プログラム言語というのは,要するに

  • なんらかの数値を保存しておき
  • なんらかの計算を行なって
  • 計算結果を表示したり保存したりする

ものです.数値を保存にはデータ変数を用い,計算や各種の作業には関数を用います.

サンプルに表示されているmain()も関数で,これはお約束により

  • プログラムが実行開始すると, main()関数を呼び出す.
  • main()関数が終了すると,プログラムは停止する.

古生代には,各種作業をmain()内部に書いていましたが,それではお友達と成果を交換することができない(mainを交換すると,作業全てが交換されてしまう.そうじゃなくて,作業の一部だけ,利用したい)ので,困ってしまいました.

ジュラ紀ぐらいになると,自分の開発した作業を,別の関数に書くようになりました(Fortran言語, C言語など).しかし,データ変数はmain()に残ったままなので,やっぱり交換できませんでした.データを保存しておく変数もいちいち交換しなければならず, 大変時間がかかったのです.

そこで,関数とデータをセットにしたらいいんじゃなイカ?と思いついた人がいました.それをオブジェクトと呼び,オブジェクトを用いたプログラムを「オブジェクト指向」と言います.

C++は,オブジェクト指向を実現した第一世代のプログラム言語です.JavaだのPythonだの新しい言語も,オブジェクト指向を実現しています.オブジェクト指向により「ファイルを保存しますか YES/NO」などを複数画面に表示しても停止しないソフトウェアが実現でき,ワードプロセッサやスマートデバイスがこの世界に出現しました.

さっそくオブジェクトを作りましょう.C++では,オブジェクトの定義をCLASSと呼びますので,クラスというのを作成します.

  • クラスってのは,例えば飛行機で言えば,「エンジン」とか「車輪」に該当する概念です.同一の「エンジン」という設計図(=クラス)に従って製造された「第一エンジン」「第二エンジン」「第三エンジン」「第四エンジン」という金属の塊が飛行機には含まれています.その実物が,オブジェクト(正確には「インスタンス」)です.

    • インスタンスは,各自の状態を保持できます.たとえば第一エンジンは「正常. 回転数9000」第二エンジンは「カラス吸い込んで回転数500」第三エンジン「被弾した」第四エンジン「脱落した」と言った感じです.それが「インスタンスは自分のデータを持っている」ということに対応します.

    • だが「飛行機」も設計図(クラス)みたいな概念であって,実物は「零号機」「初号機」「弍号機」と呼ばねば識別できないです(初号機が暴走したからと言って零号機も暴走しなければならない,ということはない).

  • 飛行機クラスの中に部品クラスが45個あって,その一つの部品クラスを構成するのが22クラスあって,その一つのクラスのパーツが7個・・・というふうに作るのがC++

    • というわけで, 45 x 22 x 7 〜 7000 個くらいの大量のクラスファイルで,飛行機が完成する.

    • 一つのクラスを見ると,「それは直径8mmのアルミ棒」「長さは50mm」「螺旋状の溝がついている」「軸周りの回転が可能である」要するに「ネジ」であったりする.簡単で,ミスが見つけやすく,その気になれば単体で機能試験ができるものを,小さいパーツから組み立てて行って,最後に長さ200mの機体になる.

ですので,ファイルがたくさんになります.是非とも,「フォルダー」を作って混乱しないようにしましょう.

VSCodeの左のフォルダー欄で二本指クリックすると

【新しいフォルダ】・・・その機体「てすとっす」の改良に必要なフォルダーを作成する

【ワークスペースにフォルダーを追加】・・・「てすとっす」以外の別の機体を作成したい.ついては,それ用のフォルダーを作成する

今は,別のプログラムを作りたいので,【ワークスペースにフォルダーを追加】にしましょう.フォルダーができたら,

【新しいファイル】で,main()を入れる「にゃんたら.cpp」 と, クラス用の「myFirstClass.hpp」とかを作ります:

クラスにファイルをひとつ作るのが基本です.1000クラスを30個のファイルに分配して書いても良いですが,そうすると「このmyFirstClassってのは,どのファイルにあるのかな」なんて無駄な時間を使うからです.

myFirstClassは, myFirstClass.hpp に書いてある.というシンプルなルールがベストです.

ファイル名違うんやけど?

C++プログラムを学ぶので,「拡張子」が cpp なのは想像がつくですが,その,hpp ってなんやねん.これは,「実装」とか「商売」とは何か,ということと関連しています.

クラスというのは設計図であると言いました.例えばM8のネジであると「それは直径8mmの棒である」「一定間隔で螺旋溝が掘ってある」とか決まっている訳です.それは「仕様」であって,それがないと,機械パーツに組み込むことができません.一方,実物は,例えば鍋屋バイテックが社運をかけて開発した炭素の格子欠陥なしで何故かネジの形になった単結晶であって,ネジ一本で5万トン支えられる,とかもありえる. そういうのは「実装」であって,どうやって5万トン支えるのかは秘密です.で, C++言語では

  • 実装は cpp ファイルに書く.販売するときには「ビルド」して(さらに特許侵害とか訴えられるようにダミーコードとか混ぜ混んで)機械語のファイルにする.
  • 仕様は hpp ファイルに書く.  仕様は公開する.公開してないと, 誰も使えないからである.

となっています.ま,大学で学生が研究する分にはcppは(現代のコンピュータでは)不要で, 全部を hpp に書く方がファイルが少なくて楽ちん.そういう書き方を「ヘッダーオンリー」と呼びます. 上で作ったのは

  • onew_main.cpp  main()が書いてある実装ファイル
  • myFirstClass.hpp     クラスのファイル.ヘッダーオンリーにする予定なので myFirstClass.cpp は作成してない

やってみる

サンプルを作ってみましょう:

では,これを使うサンプルを onew_main.cpp に作りましょう.

もちろん解説もしてくれるんだが

少し解説追加

class myFirstClass {  ←クラスの始まりは,まあ,こう書け
private: 
    int number;
public:
    myFirstClass(): number(0) {}
    ~myFirstClass() { std::cout << "ほな,ばいなら" << std::endl;}
    int setNumber(int num) { number=num; return numbet;}
    int getNumber() {return number;}
};       ←クラスの終わりは,なぜか,絶対にこう書かないとだめ.
 privateとpublic

飛行機の座席を設計する人は,エンジンのタービンの羽根の運動速度を知る必要はないです.てゆうか,教えてくれても困りますし,乗客が回転速度を勝手に変更すると,墜落する危険があります.タービンの羽根の運動速度は,エンジンがわかって居れば良いので,外部から操作できないようにする必要があります.それが「private」です.外部から操作できるものが「public」

  • 全てをpublicで書くことは,もちろん可能です.飛びます.プログラムなら,それで動作します.ただし,酔っ払った乗客の不注意な操作で墜落する危険が高くなります.プログラムだと「なんかエラーするんだけど」ってのが増えます.
変数宣言

この int number ってのは, int=整数 の,名前が number という値を使う,と宣言しています.C++では,いちおう,変数は宣言してから使うことになってます.number=12 とかの「代入」で数値を記憶できます.number+5 とかで計算もできます.なお,=というのは「等号」ではなく,「右辺の値を,左辺の値に代入してね」という命令を表していますので

   12=number

とは書かないでくださいね.

コンストラクタ

このクラス名()ってのを「コンストラクタ」と呼びます.main()プログラムでは,この設計図から「実物,沸いて出ろ!」命令で実物を作成します.

ま,さっきの  int number ってのが実物沸いて出ろ命令なんですけどね・・・「整数値をひとつ記憶できるクラスintがある」「intクラスの実物(命名number)この世に降誕せよ」
降誕した後は,number=12 ってやって活動できるようになる

で,実物が生まれた時に,自動で行う作業を定義しなくても良いですが定義できるようになっているわけね.

初期化指定子

コンストラクタの直後に:変数名(値), 変数名(値),.....と書くことで, コンストラクタが動き出す前に,内部の変数に初期値を設定できます.のちに「参照」とかを学ぶ時に必要になります.

コンストラクタ実行文

ここがコンストラクタで実行する命令をかけます.このサンプルでは,何もすることがないので{}ですが,

    myFirstClass():number(0){
      number=4;
    }

と書いても良い.この場合,最初にnumber=0で動き出しますが,直後に number=4 が実行され,myFirstClassが生まれた直後には number=4 になってます. するってえとこの部分,なくても良い気がしますが,もちろん不要です.

デストラクタ

この〜クラス名()ってのは, 実物がこの世を去る直前に実行する作業です.作業がなければ{}でよろしい.上の例では,”ほな,ばいなら”と画面に表示して終了します・・・・っとおもったら, 

VSCodeの「デバッグコンソール」自体はUTF-8の日本語表示に完全対応していません. 
どうしても日本語を表示したい場合は,外部ターミナルを利用してください

てのに引っかかってしまい,「ほな,ばいなら」が「xe9x81.......」となってしまう.VSCode内部でデバッグする時は日本語ダメ・・・訂正するのは面倒なので!しばらく日本語は避けましょう.

関数定義

ここは関数を定義しています. 関数とは,

集合A要素を一つ与えると, 集合Bの値を一つ返却するもの

です. C++では,これを

集合B 関数名(集合A 要素名)

と書きます. なので,この例では, 整数を一個受け取り, 整数を一個返却する関数を定義しています. 内容は, 受け取った整数の値を, 外部から見えない秘密のprivate変数 number にコピーするというものです. 返却する値は,ここで number の値を返却することになっています.

返却する値が存在しない場合もあります.その場合は,  集合Bとして void (つまり空虚)というのを指定します.

クラスを利用する側のmain()では

#include "myFirstClass.hpp"     ←パーツの仕様説明書を取り込む.取り込まないと寸法などがわからないから!
int main(){                     ←よくみると, main()も「関数定義」になっている
   myFirstClass obj;            ←myFirstClass設計図に基づき,実物を生成,その名称をobjとする
   obj.setNumber(42);           ←myFirstClassには命令できないが, objには命令できる. そりゃ設計図は離陸しない.飛行機は離陸するだろう.
   //  ところで, myFirstClass.setNumber() は整数を一個返却するはずだった.上の文章では, その値は無視(discard)しておる. 不要なものは廃棄でOKなのだ
   std::cout << "number=" << obj.getNumber() << std::endl;   ← objに,「お前の持っている値は」と聞いて,画面に表示
  return 0;
}

同じ設計図で,大量にインスタンスを作成しても良い:

   ...
int main(){
   myFirstClass obj_A,obj_B;
   myFirstClass obj_C;
   obj_A.setNumber(42);
   obj_C.setNumber(obj_B.setNumber(12));      //これでAくんは42, Bくんは12, Cくんは12
   std::cout << "number=" << obj_A.getNumber()+obj_B.getNumber()+obj_C.getNumber() << std::endl;
  return 0;
}

再生ボタンを押した後は, デバッグコンソール以外でも実行できるよ. ターミナルを開き

./プログラム名    と「てん すら プログラム」と入力するのがコツ. 

それで「そげなファイル,知らん」とかエラーする場合には, cd 場所  コマンドを使って,自分の作成したフォルダーに辿り着いてからやってくださいね!

File attachments
添付 サイズ
Data.zip 95.86 KB