C++言語の基礎　第3弾：クラス

# #C++言語の基礎< クラス >

##はじめに
C++言語の基礎を簡単にまとめていくことで理解を深めていくことが目的である．その第3弾として，「クラス」を扱う．

##環境
#####コンピュータ
|||
|:-:|:--|
|デバイス|MSI|
|プロセッサ|Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz 2.50GHz|
|実装RAM|8.00 GB (7.89 GB 使用可能)|
|OS|Windows (Windows 10 Home, バージョン：20H2)|

####C++
|||
|:-:|:--|
|Editor|Visual Studio Code|
|Compiler|Minimalist GNU for Windows|


##クラス
オブジェクト指向プログラミングではクラスが必ず登場する．C言語の構造体とよく似ているが，大きな違いは，**メンバとして変数と関数の両方を含めることができる**という構造である点．変数のほうをメンバ変数，関数のほうをメンバ関数と呼ぶ．

##C++言語のクラスの概要

####クラスの宣言
```c++:C++言語におけるクラスの宣言
class CSample
    {
    public:
        void function();
    private:
        int m_num;
    };
```

* CSampleがクラスの名前

####メンバ変数・メンバ関数
メンバ変数とメンバ関数とがあるが，普通の変数宣言や関数プロトタイプの書き方と同じ．サンプルでは、メンバ変数として m_numが，メンバ関数として function() が定義されている.また，特にメンバ関数のことを**メソッド**と呼ぶ．


####アクセス指定子
publicとprivateというキーワー ドは，アクセス指定子と呼ばれ，今のところはメンバ関数の宣言の前にpublic，メンバ変数の宣言の前にprivateを書くと思っておく．メンバ変数やメンバ関数が複数ある場合，次のようになる．

```c++:クラスが複数のメンバ関数，メンバ変数を持つ場合の記述方法の例
class CSample
    {
    public:
        void function();
        int  function2();
    private:
        int m_num;
        char* m_str;
    };
```


##クラスの定義とオブジェクトの生成
####オブジェクトの生成
```
CSample obj; //CSampleクラスの変数objを宣言
```
これらはインスタンスやオブジェクトなどというが，クラスから生成されたものをインスタンスということが多い．

####インスタンス
インスタンスという言葉には実体という意味があり，インスタンス化とはクラスを実体化するという意味がある．

書き方からして，クラスは単なる型に過ぎない．使うには，実体化が必要となる．メンバ変数やメンバ関数にアクセスするときには，ドット演算子(.)を使う．ポインタを経由するときはアロー演算子(->)を使う．

####簡単なサンプル
```c++:sample.h
#ifndef _SAMPLE_H_
#define _SAMPLE_H_
     
// クラス宣言
class CSample
    {
    public:
        void set(int num);  // m_numに値を設定する
        int get();          // m_numの値を取得する
    private:
        int m_num;
    };
     
#endif //_SAMPLE_H_
```
1つのプログラムで2回以上同じ宣言してはいけないというルールに従い，ヘッダファイルは1つのプログラムで1回しかインクルードできない．そこで，#ifndef, #define, #endifにより，条件コンパイルを使う．これは，記号定数というものが定義されているときは#endifに飛ぶというものである．つまり，一度#ifdef以降を通ったならば，すぐ下に記号定数を定義しているので，2回目以降は中身を無視することになる．(参考：https://www.kinjo-u.ac.jp/ushida/tekkotsu/cpp/ifndef.html )

```sample.cpp
#include "sample.h"
 
void CSample::set(int num)
    {
        m_num = num;
    }
     
int CSample::get()
    {
        return m_num;
    }
\end{lstlisting}
```
ここで，クラス宣言していたメンバ関数の中身を実装している．

```c++:main.cpp
#include <iostream>
#include "sample.cpp"
     
using namespace std;
     
int main()
    {
        CSample obj;  // CSampleをインスタンス化
        int num;
         
        cout << "整数を入力して下さい:" << endl;
        cin >> num;
         
        obj.set( num );  // CSampleのメンバ変数をセット
        cout << obj.get() << endl;  // メンバ変数の値を出力
     
        return 0;
    }
```
実行結果はキーボードから入力された数値がそのまま出力される．入力したものをクラスのメンバ変数に格納し，メンバ関数でそのメンバ変数にアクセスしていることが分かる．

##プログラムの概要
####ファイルの依存関係
通常、クラス宣言は、ヘッダファイルの中で行う．このヘッダファイルは、実装を行うsample.cppおよび、このクラスを利用するmain.cppで参照される．つまり，上記で示した条件コンパイルをしていないと2回ヘッダファイルを読み込んでしまうことになる．依存関係は下図に示すとおりである．
![depend_image](https://user-images.githubusercontent.com/61382602/116656340-7915f400-a9c7-11eb-86bd-b909f8fb9853.PNG)

####メンバ関数
C言語のプロトタイプ宣言にあたる部分は、C++言語のメンバ関数の宣言の中に記述されているが，実装は、対応する.cppファイルの中でなされる．例では，sample.cppの中で記述されている．メンバ関数の定義の仕方は関数名の頭にクラス名を付け::を挟む．CSample::setなら、CSampleクラスのsetメンバ関数 ということになり，その他のメンバ関数についても，同様である．CSampleクラスのメンバ関数の宣言と，実装の組み合わせは次のとおりである．

|||
|:-:|:--|
|宣言|実装|
|void set(int num)|void CSample::set(int num)|
|int get()|int CSample::get()|

##複数のインスタンス
クラスを用いるプログラムのメリットはインスタンスを複数作ることができることにある．以下にそのサンプルを示す．

```c++:main.cpp
#include <iostream>
#include "sample.h"
     
using namespace std;
     
int main(){
    CSample obj1,obj2;  // CSampleのインスタンスを複数生成
         
    obj1.set( 1 );  // obj1のsetメソッド呼び出し
    obj2.set( 2 );  // obj1のsetメソッド呼び出し
         
    cout << obj1.get() << endl;  // obj1のメンバ変数の値を出力
    cout << obj2.get() << endl;  // obj2のメンバ変数の値を出力
         
    return 0;
}
```
同じメソッドにアクセスし、同じメンバ変数に値を入れていますが、それぞれの実行結果は異なる．これは、同じ名前のメンバ変数、メンバ関数であっても、インスタンスが異なれば別物であることを意味する．関数についても同様である．同じ名前がついたインスタンス変数やインスタンス関数であっても、インスタンスが異なれば別物であるというのが、オブジェクト指向最大の特徴である．（下図参照）
![multi_instance_image](https://user-images.githubusercontent.com/61382602/116656862-6223d180-a9c8-11eb-9682-ee0c34fc5e15.PNG)

つまり、インスタンスが違うと、同じ名前のメンバ変数でもまったく違うものである．

##実装
上記の説明を簡単な問題を解くことで理解を深める．以下にソースコードとそのときの出力をそれぞれ示す．
なお，これらの問題は参考文献にある練習問題である．

#####ソースコード：クラス1
```c:Keisan.h
#ifndef _KEISAN_H_
#define _KEISAN_H_
 
class Keisan{
public:
  int a;
  int b;
  int add();
  int sub();
};

#endif // _KEISAN_H_
```
```Keisan.cpp
#include <iostream>
#include "keisan.h"

/*計算クラスの実装*/
int Keisan::add()
{
    return a + b;
}
int Keisan::sub()
{
    return a - b;
}
```
```c++:code_001.cpp
/*
  2021/04/24
  @Yuya Shimizu

  練習問題2-1
  クラスを使って
  4 + 3 = 7
  4 - 3 = 1
  となるようなプログラムを構成せよ
*/

#include <iostream>
#include "Keisan.cpp"

using namespace std;

int main(){
    Keisan k;
    k.a = 4;
    k.b = 3;
    cout << k.a << " + " << k.b << " = " << k.add() << endl;
    cout << k.a << " - " << k.b << " = " << k.sub() << endl;
    return 0;
}
```
#####出力
```
4 + 3 = 7
4 - 3 = 1
```
main.cppで用いるクラスの実装はKeisan.hではなく，Keisan.cppでなされているため，#includeではKeisan.hではなくKeisan.cppを指定している．もちろん，そのKeisan.cppの中ではクラスを定義したKeisan.hを参照している．

#####ソースコード：クラス2
```c:minmax.h
#ifndef _MINMAX_H_
#define _MINMAX_H_
 
class MinMax{
public:
    int max(int n1,int n2,int n3);
    int min(int n1,int n2,int n3);
};
 
#endif // _MINMAX_H_
```
```c++:minmax.cpp
#include <iostream>
#include "minmax.h"

//  最大値を返す
int MinMax::max(int n1,int n2,int n3){
  if ((n1 > n3) || (n2 > n3)){
    if(n1 > n2){
      return n1;
    }
    return n2;
    }
    return n3;
}
//  最小値を返す
int MinMax::min(int n1,int n2,int n3){
  return -max(-n1, -n2, -n3);//minはmaxの反対という意味で負符号をつけている．
}
```
```c++:code_002.cpp
/*
  2021/04/24
  @Yuya Shimizu

  練習問題2-2
    クラスMinMaxのメンバ関数max(),min()の引数の数を3つにし、期待される実行結果にならい、3つの数の最大値・最小値を出せるようにプログラムを改造せよ．
    期待される実行結果
    4と2と7のうち、最大のものは7
    4と2と7のうち、最小のものは2
*/
#include <iostream>
#include "minmax.cpp"

using namespace std;

int main(){
  MinMax m;
  int a = 4;
  int b = 2;
  int c = 7;
  cout << a << "と" << b << "と" << c << "のうち、最大のものは" << m.max(a,b,c) << endl;
  cout << a << "と" << b << "と" << c << "のうち、最小のものは" << m.min(a,b,c) << endl;
	return 0;
}
```

#####出力
```
4と2と7のうち、最大のものは7
4と2と7のうち、最小のものは2
```

##感想
Pythonでもクラスは扱っている．そのときはよく1つのプログラムファイルの中にすべて書き込んでいた．もちろん，そのクラスを他のプログラムで使うこともあった．しかしながら，C++では，そもそもクラスの宣言とその実装が分かれている．これは慣れれば見やすいと感じた．この見やすさは，デバッグのしやすさにもつながる．

##参考文献
1週間で身につくC++言語の基本　　　　　　　シフトシステム株式会社
( https://cpp-lang.sevendays-study.com/index.html ) 2021/04/24