オブジェクト指向のプログラミングには欠かせないのがクラスでしょう。クラスの基本を学ぶことはオブジェクト指向のプログラミングの第一歩と言えます。本記事ではC++でクラスを扱う基本(定義・宣言・コンストラクタ・メンバ関数)について紹介します。
クラスを使う理由
まず始めに、なぜクラスを使うといいのかという点について簡単に説明します。そんなのわかるよって方はスキップしてください。
クラスは一言で言えば変数や関数の詰め合わせです。ですので、詰め合わせる必要のない小さな開発の際にはあまり登場する機会がありません。しかし、開発の規模が大きくなるにつれて、プロダクト全体をパーツに分けて分担して開発するケースが増えます。その際に各パーツをクラスにすることでコードの可読性が上がり、開発が捗ります。これが、クラスを導入する大きなメリットです。
クラスの定義と宣言
さっそく、クラスの定義と宣言の方法について説明します。
今回はポケモンのようなモンスターRPG(?)のホゲモンの開発を想定して、その一部であるホゲモンの状態や振る舞いをクラスで表現してみましょう。状態や振る舞いなどを変数や関数で定義するのがオブジェクト指向の基本です。
タイトル:main_1.cpp
#include <string> #include <iostream> class Hogemon{ public: int index; std::string name; int level; }; int main(){ Hogemon fugachu; fugachu.index = 1; fugachu.name = "fugachu"; fugachu.level = 1; std::cout << fugachu.id << " : " << fugachu.name << std::endl; std::cout << "今のレベルは" << fugachu.level << "です。" << std::endl; }
クラスの定義
この例ではホゲモンという生き物の一つであるフガチューを表現し、それを標準出力でデバッグしています。まずは、ホゲモンというクラスを定義します。
タイトル:main_1.cpp
class Hogemon{ public: int index; std::string name; int level; };
ホゲモンにはその番号と名前とレベルという状態を定義します。定義の方法は上記のようにclass Hogemon{};
の内部で定義をし、Hogemonがクラス名となります。public:
はクラスの外部からでも変数や関数を用いることができることを意味しています。(何言ってるんだ?と思っても後ほど詳しくやるので安心してください。)そして、クラスで扱う変数(メンバデータ)を変数宣言と同じ方法で定義してます。(例: int index;
)
クラスの宣言
タイトル:main_1.cpp
Hogemon fugachu;
クラスの宣言は変数と同じように宣言するクラス名と変数名をセットにして宣言します。そして、宣言した後に、クラスのそれぞれのメンバデータに値を設定します。
タイトル:main_1.cpp
fugachu.index = 1; fugachu.name = "fugachu"; fugachu.level = 1;
上記のように変数名.メンバデータ名
のようにドットを用いてメンバデータを表すことができ、直接代入することができます。
ここまででHogemonクラスの変数fugachuの各メンバデータには値が入ったことになります。デバッグでそれを確認すると。。。
タイトル:main_1.cpp
std::cout << fugachu.index << " : " << fugachu.name << std::endl; std::cout << "今のレベルは" << fugachu.level << "です。" << std::endl;
タイトル:出力
1 : fugachu 今のレベルは1です。
このようにデータがきちんと入っていることが確認できますね。
一応これでクラスが使えました。しかし、このコードにはいくつかの改善の余地があります。
作成したクラスの問題点
まず、fugachuを作成する際に、宣言した後に直接メンバデータを代入していますが、全てのメンバデータに代入するのは手間ですね。これは次項で説明するコンストラクターを用いることで簡潔に行うことができます。
また、fugachuのメンバデータをクラスの外から自在に操作できてしまうの事は問題です。fugachu.level = 100
とする事で、仮にフガチューがレベル1だったとしても突然レベル100にすることができてしまいます。これは実際のゲームではありえないことなので、このような手続きでレベルアップができてしまう事はよくありません。そして、これは後ほどやる、メンバ関数を用いる事で解決することができます。
クラスのコンストラクター
先ほどと同じメンバデータを持ったfugachuをコンストラクターを利用して宣言してみましょう。コンストラクターを用いることでクラスの宣言の際にメンバデータへの値を代入することができます。以下がそのコードです。
タイトル:main_2.cpp
#include <string> #include <iostream> class Hogemon{ public: int index; std::string name; int level; Hogemon(int i, std::string n, int l){ index = i; name = n; level = l; } }; int main(){ Hogemon fugachu(1, "fugachu", 1); std::cout << fugachu.index << " : " << fugachu.name << std::endl; std::cout << "今のレベルは" << fugachu.level << "です。" << std::endl; }
コンストラクターの定義
コンストラクターはクラスの定義の内部で定義されます。
タイトル:main_2.cpp
Hogemon(int i, std::string n, int l){ index = i; name = n; level = l; }
コンストラクタはクラスと同じ名前の関数を定義する形で定義します。引数はクラスの宣言にコンストラクターに与えるものを意味します。関数内部でのindex
やname
,level
はクラスのメンバデータを表しています。したがって、コンストラクタの引数からメンバデータへの代入の際も方を一致させる必要があります。
コンストラクターを用いた宣言
コンストラクターを用いてクラスを宣言するのは、int型などの変数をint index(1);
として初期化するのと同様で、Hogemon fugachu(1, "fugachu", 1);
というように宣言します。これで先ほど定義したコンストラクタを用いてfugachuを宣言、初期化することができます。
タイトル:出力
1 : fugachu 今のレベルは1です。
ファイルの出力は先ほどと同じで上記のようになります。これにてコンストラクタの使い方は以上です。
クラスのメンバ関数の定義
本項ではメンバ関数を用いてメンバデータを操作する方法を紹介します。先ほどfugachuのメンバデータをクラスの外から自在に操作できてしまう事は問題と指摘しました。これを改善しつつ、さらに、fugachu.level = 100
で実現されるレベルのアップをメンバ関数を用いて実装します。
あらかじめ作成するコードを紹介しておきます。
タイトル:main_3.cpp
#include <string> #include <iostream> class Hogemon{ private: int index; std::string name; int level; public: Hogemon(int i, std::string n, int l){ index = i; name = n; level = l; } void levelUp(){ level += 1; } int getIndex(){ return index; } std::string getName(){ return name; } int getLevel(){ return level; } }; int main(){ Hogemon fugachu(1, "fugachu", 1); fugachu.levelUp(); std::cout << fugachu.getIndex() << " : " << fugachu.getName() << std::endl; std::cout << "今のレベルは" << fugachu.getLevel() << "です。" << std::endl; }
メンバデータの非公開化
まず、fugachu.level = 100
が行われることを防ぐために、メンバデータを非公開化します。これによってクラスの外部からドットを用いてメンバデータを参照することができなくなります。
タイトル:main_3.cpp
class Hogemon{ private: int index; std::string name; int level; public: //省略 };
上記のように先ほどまでpublicだったメンバデータをprivateに設定し、データを非公開にすることができます。
メンバ関数の作成
続いてfugachuのレベルをあげる処理を実装します。これにはメンバ関数を用います。メンバ関数とはクラスのメンバデータを用いて処理を行うことができる関数で、privateのメンバデータの書き換えを行うこともできます。
タイトル:main_3.cpp
class Hogemon{ private: //省略 public: Hogemon(int i, std::string n, int l){ index = i; name = n; level = l; } void levelUp(){ level += 1; } //省略 };
levelUpというメンバ関数を定義しました。この関数はクラス外部で呼び出されるため、publicにする必要があります。また、コンストラクタも外部から使うのでpublicにする必要があります。
また、メンバ関数内ではprivateのメンバデータを参照することも可能ですので、level += 1
でfugachu.level += 1
を表します。
ゲッター関数の作成
最後に、出力のために、各メンバデータを返すメンバ関数を定義する必要があります。メンバデータをそのまま返す関数のことをゲッター関数といいます。
タイトル:main_3.cpp
class Hogemon{ private: //省略 public: //省略 int getIndex(){ return index; } std::string getName(){ return name; } int getLevel(){ return level; } };
メンバ関数の呼び出し
メンバ関数はメンバデータと同じく、ドットを用いてfugachu.levelUp
のように呼び出します。
タイトル:main_3.cpp
int main(){ Hogemon fugachu(1, "fugachu", 1); fugachu.levelUp(); std::cout << fugachu.getIndex() << " : " << fugachu.getName() << std::endl; std::cout << "今のレベルは" << fugachu.getLevel() << "です。" << std::endl; }
タイトル:出力
1 : fugachu 今のレベルは2です。
これらがメンバ関数の基本的な使い方です。
この記事のまとめ
本記事ではC++でのクラスの基本的な使い方について紹介しました。最後に本記事の内容を簡単にまとめておきます。
- クラスは一言で言えば変数や関数の詰め合わせ
- クラスは定義したのち、クラス名を用いて宣言する
- クラスの宣言時にコンストラクタを用いることでメンバデータの初期化を行うことができる
- メンバデータは非公開にすることで外部からの参照を制限することができる
- メンバ関数を用いることで非公開のメンバデータも操作することができる
C++ではクラスをもっと発展的に扱うことができます。code-databaseでは順次こちらの内容の記事も作成していく予定です。是非チェックしてみてください。