プログラミング考え方講座「オブジェクト・構造体」

プログラミング考え方講座

実世界には、様々なモノがあふれている。

例えば、書店で売られている本

これを、プログラムで表現したいとしよう。

この時、ざっと考えただけでも以下の情報がある。

  • 本のタイトル
  • 著者
  • 出版年月日
  • 版数
  • 値段

これ以外にもあるだろうが、いったんこれくらいで。

で、これをプログラムで表現しようとすると、それぞれの変数を別々で宣言することになる。

…が、それでは管理が煩雑になってしまう。

そこで、複数の情報を一つにまとめておくオブジェクト構造体といった考え方が用意されている。

今回は、そんな考え方をご紹介しよう。

ここからは言語ごとの差が大きくなる部分でもあるので、本記事では考え方ごく簡単なサンプルのみご紹介しよう。

そして、今回で考え方講座は完了だ。

とはいえ、詳細な使い方はそれぞれの言語ごとに別でまとめるのでご安心を。

スポンサーリンク

今回の説明内容

今回は、オブジェクト構造体という考え方をご紹介しよう。

詳細な内容は上にも書いた通り、別で言語ごとに投稿することにする。

大体の言語に共通する考え方は今回で一通り終えられるので、最後のひと踏ん張りだ。

オブジェクト・構造体とは

オブジェクト

オブジェクトとは、複数のデータと処理を一つのまとまりにしたものだ。

例えば、の場合。上に出した例のように、1冊の本でも様々な情報を持っている

上の内容を再掲しよう。

  • 本のタイトル
  • 著者
  • 出版年月日
  • 版数
  • 値段

これを、一つのBookという種類のデータとして持つことができるのだ。

また、版数については再版されると数が変わる

これを、再版によって変更するような処理を持たせることもできる

このように、あるモノと、それに対する処理をまとめておけるのがこのオブジェクトだ。

これは、今回の3言語だとJavaJavaScriptで用いられている。

構造体

では、構造体とは何か。

こちらは、複数のデータを一つのまとまりにしたもの

オブジェクトと同じように見えるが、C言語の場合は処理が定義できない

そのため、単純なデータ保持のためのものと捉えた方がいいだろう。

上の本の例であれば、5個のデータ保持は可能だが、再版の処理は別で定義する必要がある。

なお、今回は解説していない他の言語だと、処理も記述できるようだ。

注意事項

これらの注意事項を説明していこう。

まず、これらはともに新しい型として定義される。

つまり、他のint型char型と同じように変数を用意してモノを代入するなどができる。

ただし、オブジェクトの場合はJavaやJavaScriptで使用することになるが、変数にはポインタが入る

Javaのところで解説した参照型というやつだ。

それに対し、構造体はC言語で使用するのだが、通常の変数と同じく、そのまま代入するとデータが丸々入り、ポインタとして宣言するとそのポインタを指すようになる

この違いに気を付けよう。

オブジェクト・構造体の使用例

詳細は次回以降に譲るとしても、使い方くらいは知らないとわけが分からない状態になってしまうと思う。

なので、簡単な使用例は出しておこう。

Javaの使用例(オブジェクト)

まずはJava。これはオブジェクトになる。

上で書いたような本をBookという型として定義してみよう。これをクラスと呼んだりする。

その中には上記5つのデータと、再版した際の処理、そして出力用の処理も一緒に定義してしまう。

で、メインの処理では、一つの本book1を定義し、それをもう一つの本book2にも代入しておく。

その状態で一度中身を確認し、book2のみ再版して再度両方の中身を確認している。

class Sample16 {
    public static void main(String args[]){
        Book book1 = new Book("本1", "著者1", "2020/03/23", 1, 1500);
        Book book2 = book1;

        System.out.println("book1の情報");
        book1.showBook();
        System.out.println("book2の情報");
        book2.showBook();

        book2.versionUp();
        System.out.println("book1の情報");
        book1.showBook();
        System.out.println("book2の情報");
        book2.showBook();
    }
}

class Book {
    String title;
    String author;
    String date;
    int version;
    int price;

    Book(String title, String author, String date, int version, int price){
        this.title = title;
        this.author = author;
        this.date = date;
        this.version = version;
        this.price = price;
    }

    void versionUp(){
        this.version++;
    }

    void showBook(){
        System.out.println(this.title + " : " + this.author + " : " + this.date + " : 第" + this.version + "版 : " + this.price + "円");
    }
}

早速中身を見ていこう。

まず、19行目以降を見て欲しい。ここから、Bookというクラスを定義している。

class クラス名{で、そのクラス名で新しいオブジェクトのを決めている

その中、20行目から5行分が、このBookに持たせるデータだ。そのまま変数宣言をしている。

そのあと26行目に、クラス名と同じで戻り値の型が書かれていない関数がある。

これはコンストラクタといって、このクラスに沿ったオブジェクトを作成する際に使用される専用の処理だ。

今回は5つの情報を受け取って、そのままこのモノの中身に設定している。

これの処理が終わると、呼び出し元にはこのBookという型のオブジェクトが戻される。

つまり、戻されるデータの型が決まっているから、戻り値の型をわざわざ書く必要がないのだ。

で、その中にthis.○○ = ○○;という記述が並んでいる。

○○の部分が同じだが、このイコールの左右は別のものを表している。

thisとつくと、この処理を行っている自分自身のデータを指す。

thisがついていないものは、小括弧で書いている、渡された引数を表しているのに注意しよう。

その後、34行目に再版の処理としてバージョンを一つ上げる関数、38行目に出力処理として自身の全部のデータを表示する関数を定義している。

関数、と書いてしまったが、このようにオブジェクトに定義して、それを呼び出すような形の場合、この関数をメソッドと呼ぶ。

引数戻り値の考え方は通常の関数と同じだ。

では、これらを使用している個所を見てみよう。

3行目、ここで新しいBookオブジェクトを生成している。

ここでやっているように、新しいオブジェクトを生成する場合は、new クラス名(引数);という形で書いてあげる。

このようにするとコンストラクタが呼び出され、その処理が終わったオブジェクトを得られる。

4行目では、別のBook型変数を用意して、一つ前で作ったオブジェクトを代入している…のだが。

思い出してほしい。

Javaでは、小文字で始まる、あらかじめ用意されたものだけデータが直接入っており、その他はポインタが入っていた。

つまり、ここで実際に渡しているのは、3行目で作成したオブジェクトへのポインタだ。

6行目からは普通に表示している。

ここで、オブジェクト.メソッドという形で書かれているが、オブジェクトの中身にアクセスする場合はこのようにドットを繋げて書く

つまり、7行目ではBookオブジェクトの中のshowBookというメソッドの処理を行うことを意味している。

9行目も同じだ。

で、11行目でbook2versionUpメソッドを呼び出している。

この中では、このオブジェクトのversionという情報を1増やしている。

ここで、ポインタが活きてくる。book1book2には同じオブジェクトを指すポインタが入っていた。

つまり、ここでbook2の情報を書き換えると、book1側も反映されていることになる。

その結果、12行目からの情報出力部分で、両方とも第2版と表示されるのだ。

かなりざーっと説明してきてしまったが、なんとなく掴めただろうか。

JavaScriptの使用例(オブジェクト)

普段と順番を変えて、先にJavaScriptを見てみよう。

こちらも、やり方は基本同じだ。サンプルは以下の通り。

class Book{
    constructor(title, author, date, version, price){
        this.title = title;
        this.author = author;
        this.date = date;
        this.version = version;
        this.price = price;
    }
    versionUp(){
        this.version++;
    }
    showBook(){
        console.log(this.title + " : " + this.author + " : " + this.date + " : 第" + this.version + "版 : " + this.price + "円");
    }
}

var book1 = new Book("本1", "著者1", "2020/03/23", 1, 1500);
var book2 = book1;

console.log("book1の情報");
book1.showBook();
console.log("book2の情報");
book2.showBook();

book2.versionUp();
console.log("book1の情報");
book1.showBook();
console.log("book2の情報");
book2.showBook();

Javaとの違いを以下に挙げてげておこう。

  • オブジェクトを作る前にクラスの宣言が必要
  • コンストラクタがその名の通りconstructorという名前になる
  • オブジェクトが持つデータはコンストラクタで初期値を設定することにより定義
  • メソッドは定義時にメソッド名のみでOK

その他は、book2の情報を書き換えるとbook1も変わる点も含めてJavaと同じになる。

C言語の使用例(構造体)

C言語では構造体という形になる。

実はこの構造体の定義から複数通りの定義方法があるのだが…今回はそのうち一つだけ紹介しよう。

先にサンプルを。以下のようになる。

#include<stdio.h>

struct book {
    char title[100];
    char author[20];
    char date[10];
    int version;
    int price;
};

void versionUp(struct book *book1){
    book1->version++;
}

void showBook(struct book book1){
    printf("%s : %s : %s : 第%d版 : %d円\n", book1.title, book1.author, book1.date, book1.version, book1.price);
}

int main(){
    struct book book1 = {"本1", "著者1", "2020/03/23", 1, 1500};
    struct book book2 = book1;
    struct book *book3 = &book1;

    printf("book1の情報\n");
    showBook(book1);
    printf("book2の情報\n");
    showBook(book2);

    versionUp(&book2);

    printf("book1の情報\n");
    showBook(book1);
    printf("book2の情報\n");
    showBook(book2);

    versionUp(book3);
    versionUp(book3);
    printf("book1の情報\n");
    showBook(book1);
    printf("book3の情報\n");
    showBook(*book3);


    return 0;
}

Java等とはちょっと異なる点も多いので、しっかり解説しよう。

まず3行目から、構造体を新しく定義している。

struct 構造体名 {と書くことで、構造体名を定義できる。Java等のクラス名と同じだ。

で、その中には変数のみ定義できる

これを宣言する際には、struct 構造体名 変数名のように書く必要がある。

structが必要なので気を付けよう。使用時は変数名だけでOK

その後、11, 15行目でそれぞれ関数を用意している…のだが。

11行目では、構造体のポインタを受け取り、その中のversionを一つ上げる処理をしている。

ポインタで受け取っているということは、戻った後に反映されているということだ。

それに対し、15行目では構造体のデータ自体を受け取って、その中身を表示している。

万が一誤ってこの中で操作してしまっても、元の内容には変化がない

で、19行目からメインの処理開始だ。

20行目で、新しい構造体の実体を作成しているが、C言語では元々決められているコンストラクタのようなものはない

その代わり、中括弧で配列のように、定義された順番でデータを書くことで、その値を持つモノを定義できる。

21行目では、そのままデータをbook2へ、22行目では、book1のポインタをbook3へ代入している。

この違いをよく確認しておいて欲しい。

で、先に一回情報を表示しておき、29行目でbook2のバージョンを上げている。

さて、book2には、book1データがコピーされて入っていた。

ということは、これらは別のデータなので、その後の表示では、book1第1版のままだ。

ここがオブジェクトと異なる部分なので気を付けよう。

その後、36, 37行目で、今度はbook1ポインタを渡したbook3のバージョンアップだ。

こちらは、ポインタを渡しているので実体が同じになり、book1にも変更が反映される

そのため、最後の表示では両方とも第3版となるのだ。

C言語の構造体はデータをコピーしているのか、ポインタを渡しているのかの違いが明確に分かれるので、しっかりと意識してもらいたい。

まとめ:オブジェクトと構造体

呼び方は違えど、複数の情報を一つのモノとする考え方は同じだ。

また、そのモノが入った変数が、実際はそのデータ自身を持っているのか、それとも実体へのポインタを持っているのかを気を付けよう。

今回で、共通の考え方はいったん終わりにしよう。

とはいえ、オブジェクト・構造体についてはまだまだ解説不足だ。

というわけで、次回からは言語別に特化した内容に入っていく。

まずはJavaのオブジェクトについて、より詳細な内容を見ていこう。

これがかなり奥が深く、1回で終えられるかちょっと不安だが…

更新情報はTwitterでも呟いている。よかったら、ページ下部のTwitterアイコンから覗いていってほしい。

それでは。

コメント

タイトルとURLをコピーしました