【C++】クラス

## オブジェクト指向(※超重要)
- **オブジェクト指向**とは
現実世界の「物」や「概念」を、特徴(名前、年齢など)を**変数**にしたものと
動作(移動する、話すなど)を**関数**にしたものをまとめたもの(**オブジェクト**)に対して、
指示を送ること(**指向**)でオブジェクト間の関係の定義や動作をさせる考え方。

## クラス(※抽象的に説明します)
**クラス**はオブジェクト指向の「オブジェクト」の**設計図**。
とりあえず、構造体に関数を加えたものと考えればOK!

## 構造体と比較して、異なる部分の確認
### C++
```C++: 構造体.h
#ifndef 構造体_H_INCLUDED
#define 構造体_H_INCLUDED

#include <iostream>
#include <string> // C++の文字列型
#include <format>

struct BaseInformation
{
	std::string name; // 名前
	int age;		  // 年齢
};

/// <summary>
/// 基本情報を出力する
/// </summary>
void PrintInformation(BaseInformation& baseInformation)
{
	std::cout << std::format("{}は{}歳です", baseInformation.name, baseInformation.age) << std::endl;
}

/// <summary>
/// 基本情報を出力する
/// </summary>
void InputInformation(BaseInformation& baseInformation)
{
	std::cout << "名前を入力してください" << std::endl;
	std::cin >> baseInformation.name;
	std::cout << "年齢を入力してください" << std::endl;
	std::cin >> baseInformation.age;
}

#endif // !構造体_H_INCLUDED
```

```C++: クラス.h
#ifndef クラス_H_INCLUDED
#define クラス_H_INCLUDED

#include<iostream>
#include<string> // C++の文字列型

class BaseInformationClass
{
	/// <summary>
	/// 基本情報を出力する
	/// </summary>
	void PrintInformation(void)
	{
		std::cout << std::format("{}は{}歳です", name, age) << std::endl;
	}

	/// <summary>
	/// 基本情報を入力する
	/// </summary>
	void InputInformation(void)
	{
		std::cout << "名前を入力してください" << std::endl;
		std::cin >> name;
		std::cout << "年齢を入力してください" << std::endl;
		std::cin >> age;
	}

	std::string name; // 名前
	int age;		  // 年齢
};

#endif // !クラス_H_INCLUDED
```

```C++: example.cpp
#include "構造体.h"
#include "クラス.h"

int main(void)
{
#if 1 // 1 = 構造体、0 = クラス
	BaseInformation baseInformation = { "自分の名前", 104 };

	InputInformation(baseInformation);
	PrintInformation(baseInformation);
#else
    // C#では、 = new BaseInformationClass("自分の名前", 104);になる
	BaseInformationClass baseInformationClass = { "自分の名前", 104 };

	baseInformationClass.InputInformation();
	baseInformationClass.PrintInformation();
#endif

	return 0;
}
```

### C#
```C#:example.cs
using System;

struct BaseInformation
{
    public string name; // 名前
    public int age;     // 年齢

    /// <summary>
    /// 基本情報を出力する
    /// </summary>
    public static void PrintInformation(BaseInformation baseInformation)
    {
        Console.WriteLine($"{baseInformation.name}は{baseInformation.age}歳です");
    }

    /// <summary>
    /// 基本情報を入力する
    /// </summary>
    public static void InputInformation(ref BaseInformation baseInformation)
    {
        Console.Write("名前を入力してください: ");
        baseInformation.name = Console.ReadLine();

        Console.Write("年齢を入力してください: ");
        baseInformation.age = int.Parse(Console.ReadLine());
    }
};

public class BaseInformationClass
{
    string name;
    int age;

    /// <summary>
    /// 基本情報を出力する
    /// </summary>
    public void PrintInformation()
    {
        Console.WriteLine($"{name}は{age}歳です");
    }

    /// <summary>
    /// 基本情報を入力する
    /// </summary>
    public void InputInformation()
    {
        Console.Write("名前を入力してください: ");
        name = Console.ReadLine();
        Console.Write("年齢を入力してください: ");
        age = int.Parse(Console.ReadLine());
    }
}

class Program
{
    static void Main()
    {
#if true // true = 構造体、false = クラス
        BaseInformation baseInformation = new BaseInformation { name = "自分の名前", age = 104 };

        BaseInformation.InputInformation(ref baseInformation);
        BaseInformation.PrintInformation(baseInformation);
#else
        BaseInformationClass baseInformationClass = new BaseInformationClass("自分の名前", 104 );

        baseInformationClass.InputInformation();
        baseInformationClass.PrintInformation();
#endif
    }
}
```

クラスの方では以下のエラーが
- コンストラクターのインスタンスが引数リストと一致しません
- 関数にアクセスできません

これを解決するには?...

## コンストラクタ
- 備考
`クラスが生成された際に自動で呼び出される関数`
`関数名はクラスと同名、戻り値はなしで、引数はオーバーロード可能`
**※コンストラクタ内でメンバ変数は必ず初期化**

```C++
class BaseInformationClass
{
    // コンストラクタ
    BaseInformationClass()
    {
        name = "";
        age = 0;
    }

        ・
        ・
        ・
};
```

## デストラクタ
- 備考
`クラスが破棄された際に自動で呼び出される関数`
`関数名はクラス名の前に ~ (チルダ)をつけ、戻り値はなしで、引数はオーバーロード可能`

```C++
class BaseInformationClass
{
    // デストラクタ
    ~BaseInformationClass()
    {

    }

        ・
        ・
        ・
};
```

### default
**用途 : 特殊メンバ関数が不必要であること(処理がないこと)を明示化**
```C++
class Example
{
    Example() = default;
    ~Example() = default;
}
```

### delete
**用途 : 特殊メンバ関数の使用を禁止(主にコピーコンストラクタとコピー演算子に使う)**
```C++
class Example
{
    Example(const Example& other) = delete;
    Example& operator=(const Example& other) = delete;
}
```

#### 特殊メンバ関数
`備考 : クラスのオブジェクトが生成、コピー、破棄される際に自動的に呼び出される`

```C++
class Example
{
public:
	/// <summary>
	/// コンストラクタ
	/// </summary>
	Example();

	/// <summary>
	/// デストラクタ
	/// </summary>
	~Example();

	/// <summary>
	/// コピーコンストラクタ
	/// 
	/// 既存のオブジェクトをコピーして、新しいオブジェクトを作成する
	/// </summary>
	/// <param name="other">コピー元のconst参照</param>
	Example(const Example& other);


	/// <summary>
	/// コピー代入演算子
	/// 
	/// =を使って、既存のオブジェクトをコピーして、値を代入する
	/// </summary>
	/// <param name="other">コピー元のconst参照</param>
	Example& operator=(const Example& other);

	/// <summary>
	/// ムーブコンストラクタ
	/// 
	/// 既存のオブジェクトから新しいオブジェクトに、リソースを移動する
    /// noexcept : この関数が例外を投げないことを意味する
	/// </summary>
	/// <param name="other">コピー元のオブジェクトの右値参照(&&)</param>
	Example(Example&& other) noexcept;

	/// <summary>
	/// ムーブ代入演算子
	/// 
	/// =を使って、既存のオブジェクトから新しいオブジェクトに、リソースを代入する
    /// noexcept : この関数が例外を投げないことを意味する
	/// </summary>
	/// <param name="other">コピー元のオブジェクトの右値参照(&&)</param>
	Example& operator=(Example&& other) noexcept;
};
 
```

## アクセス指定子(C++)
**用途 : クラスのメンバ(クラス内の変数や関数)へ、クラス外からアクセスできるかを指定する**

**public:** : クラス外からアクセスできる
**private:** : クラス外からアクセスできない

```C++: クラス.h
#ifndef クラス_H_INCLUDED
#define クラス_H_INCLUDED

#include<iostream>
#include<string> // C++の文字列型

class BaseInformationClass
{
public:
    // コンストラクタ
    BaseInformationClass()
    {
        name = "";
        age = 0;
    }

    // デストラクタ
    ~BaseInformationClass() = default;

	/// <summary>
	/// 基本情報を出力する
	/// </summary>
	void PrintInformation(void)
	{
		std::cout << std::format("{}は{}歳です", name, age) << std::endl;
	}

	/// <summary>
	/// 基本情報を入力する
	/// </summary>
	void InputInformation(void)
	{
		std::cout << "名前を入力してください" << std::endl;
		std::cin >> name;
		std::cout << "年齢を入力してください" << std::endl;
		std::cin >> age;
	}

	std::string name; // 名前
	int age;		  // 年齢
};

#endif // !クラス_H_INCLUDED
```

構造体はデフォルトの設定が**public**
クラスはデフォルトの設定が**private**

## アクセス修飾子(C#)
`アクセス指定子との違い : メンバ変数やメンバ関数ひとつひとつを指定する`

```C#
public class BaseInformationClass
{
    string name;
    int age;

    // コンストラクタ
    public BaseInformationClass()
    {
        name = "";
        age = 0;
    }

    // デストラクタ
    public ~BaseInformationClass()
    {

    }

    /// <summary>
    /// 基本情報を出力する
    /// </summary>
    public void PrintInformation()
    {
        Console.WriteLine($"{name}は{age}歳です");
    }

    /// <summary>
    /// 基本情報を入力する
    /// </summary>
    public void InputInformation()
    {
        Console.Write("名前を入力してください: ");
        name = Console.ReadLine();
        Console.Write("年齢を入力してください: ");
        age = int.Parse(Console.ReadLine());
    }
}
```

関数にアクセスできないエラーは解決!!
では、次。

## thisポインタ(C++)
**用途 : メンバ変数と同名の引数を扱う際に、メンバ変数に`this->`をつけ区別する**

```C++: クラス.h
#ifndef クラス_H_INCLUDED
#define クラス_H_INCLUDED

#include<iostream>
#include<string> // C++の文字列型

class BaseInformationClass
{
public:
    // コンストラクタ
    BaseInformationClass()
    {
        name = "";
        age = 0;
    }

    // コンストラクタのオーバーロード
    BaseInformationClass(std::string name, int age)
    {
        this->name = name;
        this->age = age;
    }

    // デストラクタ
    ~BaseInformationClass() = default;

	/// <summary>
	/// 基本情報を出力する
	/// </summary>
	void PrintInformation(void)
	{
		std::cout << std::format("{}は{}歳です", name, age) << std::endl;
	}

	/// <summary>
	/// 基本情報を入力する
	/// </summary>
	void InputInformation(void)
	{
		std::cout << "名前を入力してください" << std::endl;
		std::cin >> name;
		std::cout << "年齢を入力してください" << std::endl;
		std::cin >> age;
	}

	std::string name; // 名前
	int age;		  // 年齢
};

#endif // !クラス_H_INCLUDED
```

## thisキーワード(C#)
- thisポインタとの違い
thisポインタ : **オブジェクトのアドレスを指すポインタ**
thisキーワード : **オブジェクト自身**

```C#
public class BaseInformationClass
{
    string name;
    int age;

    // コンストラクタ
    public BaseInformationClass()
    {
        name = "";
        age = 0;
    }

    // コンストラクタのオーバーロード
    public BaseInformationClass(string name, int age)
    {
        this.name = name;
        this.age = age;
    }

    // デストラクタ
    public ~BaseInformationClass()
    {

    }

    /// <summary>
    /// 基本情報を出力する
    /// </summary>
    public void PrintInformation()
    {
        Console.WriteLine($"{name}は{age}歳です");
    }

    /// <summary>
    /// 基本情報を入力する
    /// </summary>
    public void InputInformation()
    {
        Console.Write("名前を入力してください: ");
        name = Console.ReadLine();
        Console.Write("年齢を入力してください: ");
        age = int.Parse(Console.ReadLine());
    }
}
```

### thisの有用性
ここまで解説しましたが、かなり賛否両論な機能です。
なぜなら、thisを付け忘れ、バグとなり、
かつバグの中でも見つけにくいものに部類されるからです。
なので、thisを使わず、引数名の前に_(アンダーバー)をつけ、
区別するエンジニアが多いです。

### メンバイニシャライザ(C++)
**用途 : constメンバ変数、 参照メンバ変数の初期化**

**メンバイニシャライザ**は{}内で初期化するよりも、速いタイミングで初期化を行う

```C++
// コンストラクタのオーバーロード
BaseInformationClass(std::string _name, int _age)
	: name(_name), age(_age)
{}
```

## 完成?
構造体と同じ様に動くようになったので、これで解決なんですが...

## カプセル化(※超重要)
**用途 : データの安全性の確保、コードの保守性向上、再利用性の向上**
`備考 : クラスの三大要素(カプセル化、継承、多態性)の1つ。`

#### C++
```C++: クラス.h
#ifndef クラス_H_INCLUDED
#define クラス_H_INCLUDED

#include<iostream>
#include<string> // C++の文字列型

class BaseInformationClass
{
public:
    // コンストラクタ
    BaseInformationClass()
    {
        name = "";
        age = 0;
    }

    // コンストラクタのオーバーロード
    BaseInformationClass(std::string _name, int _age)
    {
        name = _name;
        age = _age;
    }

    // デストラクタ
    ~BaseInformationClass() = default;

	/// <summary>
	/// 基本情報を出力する
	/// </summary>
	void PrintInformation(void)
	{
		std::cout << std::format("{}は{}歳です", name, age) << std::endl;
	}

	/// <summary>
	/// 基本情報を入力する
	/// </summary>
	void InputInformation(void)
	{
		std::cout << "名前を入力してください" << std::endl;
		std::cin >> name;
		std::cout << "年齢を入力してください" << std::endl;
		std::cin >> age;
	}

private:
	std::string name; // 名前
	int age;		  // 年齢
};

#endif // !クラス_H_INCLUDED
```

#### C#
```C#
public class BaseInformationClass
{
    private string name; // 名前
    private int age;     // 年齢

    // コンストラクタ
    public BaseInformationClass()
    {
        name = "";
        age = 0;
    }

    // コンストラクタのオーバーロード
    public BaseInformationClass(string _name, int _age)
    {
        name = _name;
        age = _age;
    }

    // デストラクタ
    public ~BaseInformationClass()
    {

    }

    /// <summary>
    /// 基本情報を出力する
    /// </summary>
    public void PrintInformation()
    {
        Console.WriteLine($"{name}は{age}歳です");
    }

    /// <summary>
    /// 基本情報を入力する
    /// </summary>
    public void InputInformation()
    {
        Console.Write("名前を入力してください: ");
        name = Console.ReadLine();
        Console.Write("年齢を入力してください: ");
        age = int.Parse(Console.ReadLine());
    }
}
```

### ゲッター(Get〇〇)、セッター(Set〇〇)
**用途 : privateのメンバ変数を取得や変更**

#### C++
```C++
public:

// 名前の取得・変更
inline std::string GetName(void) { return name; }
inline void SetName(std::string _name) { name = _name; }

// 年齢の取得・変更
inline int GetAge(void) { return age; }
inline void SetAge(int _age) { age = _age; }
```

#### C#
```C#
// 名前の取得・変更
public string Name
{
    get { return name; }
    set { name = value; }
}

// 年齢の取得・変更
public int Age
{
    get { return age; }
    set { age = value; }
}
```

### constメンバ関数(C++)
**用途 : 関数内で値の変更を不可能にする**

#### C++
```C++
public:

// 名前の取得
inline std::string GetName(void) const { return name; }

// 年齢の取得
inline int GetAge(void) const { return age; }
```

## 静的メンバ
**用途 : インスタンス生成の省略(クラス全体で共有)**

### 静的メンバ変数と静的メンバ関数
#### C++
```C++:example.cpp
#include <iostream>

class Example
{
public:
	/// <summary>
	/// 静的メンバ関数
	/// </summary>
	inline static int GetAddNumber(int number2)
	{
		return number + number2;
	}

private:
	static int number; // 静的メンバ変数
};
// 静的メンバ変数の初期化
int Example::number = 0;

int main(void)
{
	// 出力
	printf("%d\n", Example::GetAddNumber(10));

	return 0;
}
```

#### C#
```C#:example.cs
using System;

public class Example
{
    private static int number = 0; // 静的メンバ変数

	/// <summary>
	/// 静的メンバ関数
	/// </summary>
	public static int GetAddNumber(int number2)
	{
		return number + number2;
	}
};

class Program
{
    static void Main()
    {
        Console.WriteLine($"{Example.GetAddNumber(10)}");
    }
}
```

**注意点**
1. 静的メンバ変数は**クラス外で初期化**(C++)
(クラスが呼び出された時、その度に初期化されるため)
2. 静的メンバ関数内では**静的メンバ変数または引数のみ使用可能**
また、**thisは使用できない**
(インスタンスを生成しないため)