オブジェクト指向(※超重要)
-
オブジェクト指向とは
現実世界の「物」や「概念」を、特徴(名前、年齢など)を変数にしたものと
動作(移動する、話すなど)を関数にしたものをまとめたもの(オブジェクト)に対して、
指示を送ること(指向)でオブジェクト間の関係の定義や動作をさせる考え方。
クラス(※抽象的に説明します)
クラスはオブジェクト指向の「オブジェクト」の設計図。
とりあえず、構造体に関数を加えたものと考えればOK!
構造体と比較して、異なる部分の確認
C++
#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
#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
#include "構造体.h"
#include "クラス.h"
int main()
{
#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#
using System;
struct BaseInformation
{
public string name; // 名前
public int age; // 年齢
};
/// <summary>
/// 基本情報を出力する
/// </summary>
static void PrintInformation(BaseInformation baseInformation)
{
Console.WriteLine($"{baseInformation.name}は{baseInformation.age}歳です");
}
/// <summary>
/// 基本情報を入力する
/// </summary>
static void InputInformation(ref BaseInformation baseInformation)
{
Console.Write("名前を入力してください: ");
baseInformation.name = Console.ReadLine();
Console.Write("年齢を入力してください: ");
baseInformation.age = int.TryParse(Console.ReadLine());
}
public class BaseInformationClass
{
string name;
int age;
/// <summary>
/// 基本情報を出力する
/// </summary>
void PrintInformation()
{
Console.WriteLine($"{name}は{age}歳です");
}
/// <summary>
/// 基本情報を入力する
/// </summary>
void InputInformation()
{
Console.Write("名前を入力してください: ");
name = Console.ReadLine();
Console.Write("年齢を入力してください: ");
age = int.Parse(Console.ReadLine());
}
}
static int Main(string[] args)
{
#if 1 // 1 = 構造体、0 = クラス
BaseInformation baseInformation = new BaseInformation { name = "自分の名前", age = 104 };
InputInformation(ref baseInformation);
PrintInformation(baseInformation);
#else
BaseInformationClass baseInformationClass = new BaseInformationClass("自分の名前", 104 };
baseInformationClass.InputInformation();
baseInformationClass.PrintInformation();
#endif
}
クラスの方では以下のエラーが
- コンストラクターのインスタンスが引数リストと一致しません
- 関数にアクセスできません
これを解決するには?...
コンストラクタ
- 備考
クラスが生成された際に自動で呼び出される関数
関数名はクラスと同名、戻り値はなしで、引数はオーバーロード可能
※コンストラクタ内でメンバ変数は必ず初期化
class BaseInformationClass
{
// コンストラクタ
BaseInformationClass()
{
name = "";
age = 0;
}
・
・
・
};
デストラクタ
- 備考
クラスが破棄された際に自動で呼び出される関数
関数名はクラス名の前に ~ (チルダ)をつけ、戻り値はなしで、引数はオーバーロード可能
class BaseInformationClass
{
// デストラクタ
~BaseInformationClass()
{
}
・
・
・
};
default
用途 : 特殊メンバ関数が不必要であること(処理がないこと)を明示化
class Example
{
Example() = default;
~Example() = default;
}
delete
用途 : 特殊メンバ関数の使用を禁止(主にコピーコンストラクタとコピー演算子に使う)
class Example
{
Example(const Example& other) = delete;
Example& operator=(const Example& other) = delete;
}
特殊メンバ関数
備考 : クラスのオブジェクトが生成、コピー、破棄される際に自動的に呼び出される
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: : クラス外からアクセスできない
#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#)
アクセス指定子との違い : メンバ変数やメンバ関数ひとつひとつを指定する
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->
をつけ区別する
#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キーワード : オブジェクト自身
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メンバ変数、 参照メンバ変数の初期化
メンバイニシャライザは{}内で初期化するよりも、速いタイミングで初期化を行う
// コンストラクタのオーバーロード
BaseInformationClass(std::string _name, int _age)
: name(_name), age(_age)
{}
完成?
構造体と同じ様に動くようになったので、これで解決なんですが...
カプセル化(※超重要)
用途 : データの安全性の確保、コードの保守性向上、再利用性の向上
備考 : クラスの三大要素(カプセル化、継承、多態性)の1つ。
C++
#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#
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++
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#
// 名前の取得・変更
public string Name()
{
get { return name; }
set { name = value; }
}
// 年齢の取得・変更
public int Age()
{
get { return age; }
set { age = value; }
}
constメンバ関数(C++)
用途 : 関数内で値の変更を不可能にする
C++
public:
// 名前の取得
inline std::string GetName(void) const { return name; }
// 年齢の取得
inline int GetAge(void) const { return age; }
静的メンバ
用途 : インスタンス生成の省略(クラス全体で共有)
静的メンバ変数と静的メンバ関数
C++
#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()
{
// 出力
printf("%d\n", Example::GetAddNumber(10));
return 0;
}
C#
using System;
public class Example
{
private static int number = 0; // 静的メンバ変数
/// <summary>
/// 静的メンバ関数
/// </summary>
public static int GetAddNumber(int number2)
{
return number + number2;
}
};
static int Main(string[] args)
{
Console.WriteLine($"{Example.GetAddNumber(10)}");
}
注意点
- 静的メンバ変数はクラス外で初期化(C++)
(クラスが呼び出された時、その度に初期化されるため) - 静的メンバ関数内では静的メンバ変数または引数のみ使用可能
また、thisは使用できない
(インスタンスを生成しないため)