C++言語の基礎　第4弾：アクセス指定子

# #C++言語の基礎< アクセス指定子 >

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

##環境
#####コンピュータ
|||
|:-:|:--|
|デバイス|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|

##アクセス指定子
####アクセス指定子の種類
クラス宣言でpublicやprivateというキーワードを使ったが，これらは**アクセス指定子**と言い、メンバ変数やメンバ関数にアクセスできる範囲を指定することが出来る．

|||
|:-:|:--|
|アクセス指定子|意味|
|public|すべての範囲から呼び出し・読み出し可能|
|private|同一クラスまたは同一インスタンス内でのみアクセス可能|
|protected|同一クラスまたは同一インスタンスおよび，サブクラスおよびそのインスタンス内でのみアクセス可能|

####アクセス指定子の有効範囲
ここでは、privateおよび、public修飾子の働きを見ていく．

```c:Sample.h
#ifndef _SAMPLE_H_
#define _SAMPLE_H_
  
class Sample{
  public:
    int a;      //  publicなメンバ変数
  private:
    int b;      //  privateなメンバ変数
  public:
    void func1();
  private:
    void func2();
  };
     
    #endif // _SAMPLE_H_
```

```c++:Sample.cpp
#include "Sample.h"
#include <iostream>
     
using namespace std;
     
void Sample::func1(){
    cout << "func1" << endl;
    a = 1;
    b = 1;
    func2();                    //  func2ないから、func1を呼び出す
}
void Sample::func2(){
    a = 2;
    b = 2;
    cout << "a=" << a  << "," << "b=" << b << endl;
}
```

```c++:main.cpp
#include "Sample.cpp"
#include <iostream>
     
using namespace std;
     
int main(){
    Sample s;
    s.a = 1;
    //s.b = 2;
    s.func1();
    //s.func2();
}
```
main.cppを見てもわかる通り，publicなメンバ変数、およびメンバ関数である a，func1()は，クラスの外部であるmain.cppからアクセスすることが可能．しかし、main.cppの9行目，および11行目のコメントをとってみると，ビルドエラーが出るはず．このように，private修飾子がついているメンバ変数，およびメンバ関数はクラスの外からアクセスできない．イメージを下図に示す．
![access_image](https://user-images.githubusercontent.com/61382602/116689067-92806580-a9f2-11eb-9743-d630e03ddb7c.PNG)

##カプセル化
####カプセル化とアクセスメソッド
メンバへのアクセスを，そのクラスのメンバ関数からだけしかできないように制限することを**カプセル化**という．外からそのメンバ変数へアクセスすることが出来ない．そこで，そのメンバ変数の値を設定したり，取得したりするメンバ変数が必要になる．それを，アクセスメソッドなどと呼ぶ．

```c:sample.h
class CSample
  {
  public:
      void setNum(int num);  // private指定されたメンバ変数に書き込む
      int getNum();          // private指定されたメンバ変数を読み取る
  private:
      int m_num;
  };
```

```c++:sample.cpp
#include "sample.h"
 
void CSample::setNum(int num)
{
    m_num = num;
}
int CSample::getNum()
{
    return m_num;
}
```

```c++:main.cpp
#include <iostream>
#include "sample.h"
       
using namespace std;
       
int main(){
    CSample s;
    s.setNum(5);
    cout << s.getNum() << endl;
    return 0;
}
```

####セッターとゲッター
一般に，メンバ変数に書き込みを行うメンバ関数を**セッター**，読み込みを行うメンバ関数を**ゲッター**という．そのイメージは下図である．

![getter_setter_image](https://user-images.githubusercontent.com/61382602/116689073-93b19280-a9f2-11eb-813a-d6c20844a9db.PNG)

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

#####ソースコード：セッター・ゲッター１
```c:data.h
#ifndef _CDATA_H_
#define _CDATA_H_
 
#include <iostream>
#include <string>
 
using namespace std;
 
class CData{
//private メンバ
private:
    //  メンバ変数number
    int number;
    //  メンバ変数comment
    string comment;
//public メンバ
public:
    //  初期化
    void init();
    //  メンバ関数setComment
    void setComment(string str);
    //  メンバ関数setNumber
    void setNumber(int num);
    //  メンバ関数getComment
    string getComment();
    //  メンバ関数getNumber
    int getNumber();
};
 
#endif // _CDATA_H_
```
```c++:data.cpp
#include "data.h"
 
void CData::init()
{
    number = 0;
    comment = "";
}

void CData::setComment(string str){
  comment = str;
}

void CData::setNumber(int num){
  number = num;
}

string CData::getComment(){
  return comment;
}

int CData::getNumber(){
  return number;
}
```
```c++:prob_001.cpp
/*
  2021/04/25
  @Yuya Shimizu

  問題3-1
    クラスCDataにメンバ変数のセッターおよびゲッターを追加し，ビルドが通るようにし，期待される実行結果を得られるようにしなさい．
    期待される実行結果
    number = 100 comment = Programming C++
*/
#include <iostream>
#include "data.cpp"
 
using namespace std;
 
int main(){
    CData d;
    d.init();   //  パラメータを初期化
    d.setNumber(100);
    d.setComment("Programming C++");
    cout << "number = " << d.getNumber() << " comment = " << d.getComment() << endl;
    return 0;
}
```
#####出力
```
number = 100 comment = Programming C++
```

#####ソースコード：セッター・ゲッター2
```c:twostrings.h
#ifndef _TWOSTRINGS_H_
#define _TWOSTRINGS_H_
 
#include <iostream>
#include <string>
 
using namespace std;
 
class TwoStrings{
private:
    //  一つ目の文字列
    string m_string1;
    //  二つ目の文字列
    string m_string2;
public:
    //  一つ目の文字列を設定
    void setString1(string s);
    //  二つ目の文字列を設定
    void setString2(string s);
    //  一つ目の文字列を取得
    string getString1();
    //  二つ目の文字列を取得
    string getString2();
    //  二つの文字列をつないだものを取得
    string getConnectedString();
    //  getConnectedString()で得られる文字列の長さを取得
    int getConnectedLength();
};
 
#endif // _TWOSTRINGS_H_
```
```c++:twostrings.cpp
#include"twostrings.h"

void TwoStrings::setString1(string s){
  m_string1 = s;
}

void TwoStrings::setString2(string s){
  m_string2 = s;
}

string TwoStrings::getString1(){
  return m_string1;
}

string TwoStrings::getString2(){
  return m_string2;
}

string TwoStrings::getConnectedString(){
  return m_string1 + m_string2;
}

int TwoStrings::getConnectedLength(){
  return TwoStrings::getConnectedString().length();
}
```
```c++:prob_002.cpp
/*
  2021/04/25
  @Yuya Shimizu

  問題3-2
    二つの文字列を合成したり，その長さを求める機能を持つクラス．このクラスのソースファイルである，twostring.cppを完成させ，実行結果の通りに動くようにしなさい．
*/
#include <iostream>
#include "twostrings.cpp"
 
using namespace std;
 
int main(){
    TwoStrings s;
    s.setString1("Hello");
    s.setString2("World");
    cout << "一つ目の文字列は" << s.getString1() << endl;
    cout << "二つ目の文字列は" << s.getString2() << endl;
    cout << "二つの文字列を合成したものは" << s.getConnectedString() << endl;
    cout << "合成した文字列の長さは" << s.getConnectedLength() << "文字" << endl;
    return 0;
}
```
#####出力
```
一つ目の文字列はHello
二つ目の文字列はWorld
二つの文字列を合成したものはHelloWorld
合成した文字列の長さは10文字
```

#####ソースコード：セッター・ゲッター3
```c:calculation.h
#ifndef _CALCULATION_H_
#define _CALCULATION_H_

class Calculation{
  private:
    int m_number1;  //1つ目の数
    int m_number2;  //2つ目の数
  
  public:
    void setNumber1(int n);   //m_number1のセッター
    void setNumber2(int n);   //m_number2のセッター
    int getNumber1();         //m_number1のゲッター
    int getNumber2();         //m_number2のゲッター
    int add();                //m_number1とm_number2の和
    int sub();                //m_number1とm_number2の差
};

#endif
```
```c++:calculation.cpp
#include"calculation.h"

//m_number1のセッター
void Calculation::setNumber1(int n){
  m_number1 = n;
}
//m_number2のセッター
void Calculation::setNumber2(int n){
  m_number2 = n;
}
//m_number1のゲッター
int Calculation::getNumber1(){
  return m_number1;
}
//m_number2のゲッター
int Calculation::getNumber2(){
  return m_number2;
}
//m_number1とm_number2の和
int Calculation::add(){
  return m_number1 + m_number2;
}
//m_number1とm_number2の差
int Calculation::sub(){
  return m_number1 - m_number2;
}
```
```c++:prob_003.cpp
/*
  2021/04/25
  @Yuya Shimizu

  問題3-3
    仕様をもとに，クラスCalculationを構成するヘッダファイルcalculation.hおよびソースファイルcalculation.cppを完成させ，動作するプログラムを完成させなさい．
*/
#include <iostream>
#include "calculation.cpp"
 
using namespace std;
 
int main(){
    Calculation c;
    c.setNumber1(8);    //  一つ目の数をセット
    c.setNumber2(9);    //  二つ目の数をセット
    //  二つの数の和を表示
    cout << c.getNumber1() << " + " << c.getNumber2() << " = " << c.add() << endl;
    //  二つの数の差を表示
    cout << c.getNumber1() << " - " << c.getNumber2() << " = " << c.sub() << endl;
    return 0;
}
```
#####出力
```
8 + 9 = 17
8 - 9 = -1
```

##感想
以上の練習で，セッターとゲッター，およびアクセス指定子についての基礎は理解できたのではないかと思う．改めてまとめると，アクセス指定子privateはクラスの中でのみアクセスでき，サブクラスまで範囲を広げたのがprotected，すべての範囲からのアクセスを可能にしたのがpublicであることを学んだ．

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