クラスや構造体についてまとめてみる

<!-- クラスや構造体についてまとめてみる -->

## はじめに

プログラミング言語、特にオブジェクト指向のものを扱うとき、クラスや構造体の考え方が非常に重要になってくることは知っていたのですが、授業の際に扱うプログラムで軽く書いてみたくらいで、しっかりと理解したり使ってみたりはしていませんでした。

今回はクラスや構造体について改めてまとめなおし、例をいろいろ書いてみようと思います。

## クラス

クラスとは、「オブジェクトがどのような特性・データ・機能を持っているかを定義したもの」です。オブジェクトとはプログラム内で扱う実体を意味します。

クラスを定義することにより、共通の特性や動作を持つオブジェクトを簡単に量産することができます。クラスをもとにして生成されたオブジェクトをインスタンスと呼びます。これらのインスタンスはそれぞれ異なる状態を持つことができます。

オブジェクト指向プログラミングにおいて必要不可欠な概念であり、クラスを使うことによって、プログラムの可読性や保守性が向上します。例えば、一度クラスを定義してしまえば、同じクラスを使って何度でもインスタンスを生成することができるため、同じようなコードを書く必要がなくなり、冗長性を減らすことができます。

クラスは以下のような要素を持つことができます。

- 属性
  - オブジェクトの状態を表す変数
  - クラス内で定義され、インスタンスごとに異なる値を持つことができる
- クラスメソッド
  - オブジェクトの動作を定義する関数
  - クラス内で定義され、インスタンスごとに異なる動作を持つことができる
  - コンストラクタ
    - インスタンスが生成されるときに呼び出される特別なメソッド
    - インスタンスの初期化を行う
  - デストラクタ
    - インスタンスが破棄されるときに呼び出される特別なメソッド
    - インスタンスの後処理を行う

総じて、クラスはいくつかの変数や関数をまとめたものと考えることができます。

## 構造体

構造体は複数のデータをまとめて一つのデータ型として扱うためのものです。要素ごとの型を決める事のできる配列の様なものと考えることもできます。

まとめるデータの型や種類は自由に決めることができます。構造体自体の名前も自分で決定することができるので、自作の特殊なデータ型を作成していると考えて差し支えないです。

これは特にデータ型を重要視する言語で有効となります。入ってくるデータの型を明示的に指定しながらそれらを管理することができるので、色々と便利です。型が間違っていてエラーが出ることを防止する事にもつながります。

## 例

では、クラスや構造体を用いていろいろ作ってみましょう。

今回はPython、C、C++、Rubyを使って作ってみました。

### Pythonを使った例

Pythonではクラスが構造体を兼ねているので、構造体はクラスを用いて実装できます。

```python

class Person:
    def __init__(self):
        self.name = ""
        self.age = 0
    def print_profile(self):
        print(f"Name: {self.name}")
        print(f"Age: {self.age}")
    def add_age(self):
        self.age += 1

class ProductInfo:
    def __init__(self):
        self.name = ""
        self.price = 0
        self.comments = [""]

def main():
    person = Person()
    person.name = "Mr. Qiita"
    person.age = 15
    person.print_profile()
    product = ProductInfo()
    product.name = "Product A"
    product.price = 1000
    product.comments = ["Good", "About IT"]
    print(product.price * 5)

if __name__=="__main__":
    main()

```

```bash
$ python3 class_and_structure.py
Name: Mr. Qiita
Age: 15
5000
```

``Person``クラスは名前と年齢をクラス変数として持ち、``print_profile``メソッドでそれらを表示します。``add_age``メソッドで年齢を1歳増やすことができます。

``ProductInfo``クラスは商品名、価格、コメントをクラス変数として持ちます。

コンストラクタである``__init__``メソッドにより、これらのクラスが呼び出された時に自動的にクラス変数が生成・初期化されます。

ざっくり言うと、クラス内に変数とメソッドを併せ持つのがクラスであり、構造体は変数だけを持つものと考えることができます。

``main``関数内で、``Person``クラスと``ProductInfo``クラスのインスタンスを生成し、それぞれに対して操作を行っています。前者は自前のメソッドを持っているのでそれを用いた操作ができ、後者はクラス変数を直接操作して保管されたデータをいじるという形になっています。

### Cを使った例

逆に、C言語では構造体しかありません。クラスのようなものを作るためには、構造体と関数を組み合わせて実装する必要があります。

```c
#include <stdio.h>

typedef struct person
{
    char name[50];
    int age;
} Person;

void print_profile(const Person *p)
{
    printf("Name: %s\n", p->name);
    printf("Age: %d\n", p->age);
}

void add_age(Person *p)
{
    p->age++;
}

typedef struct productinfo
{
    char name[50];
    int price;
    char comments[50][100];
} ProductInfo;

int main(void)
{
    Person qiita = {"Mr. Qiita", 15};
    Person *p = &qiita;
    print_profile(p);
    ProductInfo product = {"Product A", 1000, {"Good", "About IT"}};
    printf("%d\n", product.price * 5);
    return 0;
}
```

```bash
$ gcc class_and_structure.c -o class_and_structure && ./class_and_structure
Name: Mr. Qiita
Age: 15
5000
```

``typedef``を使って構造体の型を定義し、``struct``を使って構造体を定義しています。

構造体内でいわゆるクラス変数を定義・管理し、クラス変数は別途定義して扱うことになります。

また、定義した構造体のポインタを用いないと、構造体のメンバにアクセスできないので注意が必要です。

### C++を使った例

C++ではクラスと構造体の両方を用いることができます。

```cpp
#include <iostream>
#include <string>
using namespace std;

class Person
{
public:
    string name;
    int age;
    void print_profile()
    {
        cout << "Name: " << name << "\nAge: " << age << endl;
    }
    void add_age()
    {
        age++;
    }
};

typedef struct productinfo
{
    string name;
    int price;
    string comments[50];
} productinfo;

int main(void)
{
    Person person;
    person.name = "Mr. Qiita";
    person.age = 15;
    person.print_profile();
    productinfo product;
    product.name = "Product A";
    product.price = 1000;
    product.comments[0] = "Good";
    product.comments[1] = "About IT";
    cout << product.price * 5 << endl;
    return 0;
}
```

```bash
$ g++ class_and_structure.cpp -o class_and_structure && ./class_and_structure
Name: Mr. Qiita
Age: 15
5000
```

### Rubyを使った例

Rubyでもクラスと構造体の両方を用いることができます。

特に、構造体は専用のクラスが存在しているので、それを用いて実装しますが、クラスを定義する書き方と似通った点があります。

```ruby
class Person
  def initialize(name, age)
    @name = name
    @age = age
  end
  def print_profile()
    puts "Name: #{@name}"
    puts "Age: #{@age}"
  end
  def add_age()
    @age += 1
  end
end

class Product < Struct.new(:name, :price, :comments)
end

  def main
  person = Person.new("Mr. Qiita", 15)
  person.print_profile
  product = Product.new("Product A", 1000, ["Good", "About IT"])
  puts product.price * 5
end

main()
```

```bash
$ ruby class_and_structure.rb
Name: Mr. Qiita
Age: 15
5000
```

Pythonと似たような感じですね。

``initialize``メソッドでインスタンスを生成する際に初期化を行っています。この時、``@``をつけているのはグローバルで、つまりクラスの外からも扱えるようにするためです。

構造体の場合、変数はシンボルで定義します。

## まとめ

クラスや構造体を使うことはオブジェクト指向プログラミングにおいて基本的なことであり、身に付けなればならないスキルです。

これらの概念をしっかり理解することで、可読性や保守性、再利用性の高いプログラムを書くことができるようになります。

書いたプログラムたちは以下のリポジトリにまとめました。実際に実行して参考にしてみてください。

https://github.com/Gitudon/Test-Repositories/tree/master/Class-and-Structure

それではまた、次の機会にお会いしましょう。

## 参考文献

[クラスとは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word1120.html)

[構造体とは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word13243.html)

[Pythonのクラスについて #初心者 - Qiita](https://qiita.com/eric50905/items/3030cfa03f849fec04dd)

[C言語でオブジェクト指向を表現する (クラス、継承) #C - Qiita](https://qiita.com/qiita_kuru/items/8f3441bce9b2f53d1d62)

[C++言語の基礎　第3弾：クラス #class - Qiita](https://qiita.com/Yuya-Shimizu/items/45d42fe2942a684fa96a)

[c++ 構造体の諸々 No3 #C++ - Qiita](https://qiita.com/sesame0224/items/9866629c2cedc3f6c904)

[【Ruby】Struct(構造体クラス)を理解する #Ruby - Qiita](https://qiita.com/k-penguin-sato/items/54189d5ed4e5f7463266)

[Ruby クラスの基礎知識まとめ #class - Qiita](https://qiita.com/nk-ty/items/654b285a7bcbd82b1cff)