C++講座
もくじ
第2章『クラス』
第二の壁�ポインタ
クラスの宣言
クラス宣言はふつうヘッダファイルにまとめる
#pragma once
class Pinkri{
public:
int hp;
int mp;
};
ぴんくりクラスの宣言
アクセス修飾子(public)
以降に書いたデータはpublic
(他のオブジェクトからも�参照できるようになる)
int型のhp、mpを宣言
コンパイラ一度しか読まないの呪文�(Visual Studioのみ)
Pinkri型を作れる
HP=?
MP=?
Pinkri
クラスからオブジェクトを作る
ぴんくり型の変数pinを作る
int main(){
Pinkri pin;
pin.hp = 100; // pinのHPは100
pin.mp = 50; // pinのMPは50
}
HP=100
MP=50
pin
オブジェクトの初期化と後処理
class Pinkri{
public:
int hp;
int mp;
Pinkri(int h, int m);
~Pinkri();
};
コンストラクタ(初期化)
初めに呼ばれる関数
HPとMPの初期化をする
デストラクタ(後処理)
終了時に呼ばれる関数
今は何もしないが一応作る
クラスの実装
クラスで宣言した関数の中身は
ふつうソースファイルで実装する
Pinkri::Pinkri(int h, int m){
hp = h;
mp = m;
}
Pinkri::~Pinkri(){
// 何もしない
}
引数(関数f(x)でいうx)
ぴんくりのHP、MPに
それぞれ与えられた
引数の値を代入
クラスの実装
これで初期化が簡単になる
int main(){
Pinkri pin(100, 50);
}
コンストラクタの引数に
100, 50を与えたことで
pin.hp = 100, pin.mp = 50
になった
メンバ関数(メソッド)
class Pinkri{
public:
int hp;
int mp;
Pinkri(int h, int m);
~Pinkri();
void Cure();
};
回復魔法Cure()を追加�MPを5消費して�自分のHPを20回復する
関数の実装
関数の中身をソースファイルで実装する
void Pinkri::Cure(){
if(mp < 5){
std::cout << “MPが足らない!” << endl;
}else{
mp -= 5;
hp += 20;
std::cout << “HPが20回復した!” << endl;
}
}
関数の実装
実際にぴんくりを宣言して呼び出す
int main(){
Pinkri pin();
pin.Cure();
}
関数の呼び出しも
データと同様、変数の後に
「.」を付けて記す
関数の引数と戻り値
引数:関数を実行するときに用いるパラメータ
戻り値:関数の実行結果で返す値
例)y=√x … 引数はx、戻り値はy
int nibai(int x){
int y = x * 2;
return y;
}
引数(int型)の値を
2倍にして返す関数
戻り値の型はint
引数xをもとに
戻り値yを計算し、
yを戻り値とする
関数の引数と戻り値
さっき作った2倍関数を使ってみる例
int main(){
int a = nibai(30);
int b = 50;
int c = nibai(b);
}
int nibai(int x){
int y = x * 2;
return y;
}
aの値:60
bの値:50
cの値:100
ポインタ
コンピュータのデータはメモリに格納される
int main(){
int a = 50;
}
0x100001:
0x100002:
0x100003:50
0x100004:
0x100005:
0x100006:
変数aが宣言されると、コンピュータ(OS)が
自動で未使用のメモリを見つけてくれて、
そこにデータを格納して取り扱っている。
ポインタ
ポインタはメモリのアドレスを格納する変数
int main(){
int a = 50;
int *p = &a;
}
0x100001:
0x100002:
0x100003:50
0x100004:0x100003
0x100005:
0x100006:
変数aのアドレス0x100003の値が
変数pに代入され、ここに格納された
ポインタ
アドレスの値を直接操作することもできる
int main(){
int a = 50;
int *p = &a;
*p = 100;
}
0x100001:
0x100002:
0x100003:100
0x100004:0x100003
0x100005:
0x100006:
変数aのアドレス0x100003の値に
100を代入したから、aの値が変わった
ポインタの記号
int *p;
ポインタ変数pを宣言(int* pでもいい)
&a
aのアドレス
*p
ポインタpの指す値
(宣言時の*とは別物なので注意)
ポインタと配列
配列の正体はポインタである
int main(){
int a[5];
for(int i=0; i<5; i++){
a[i] = i + 1;
}
}
0x100001:1
0x100002:2
0x100003:3
0x100004:4
0x100005:5
0x100006:
ポインタと配列
配列はポインタの処理と同等に扱える
int main(){
int a[5];
for(int i=0; i<5; i++){
*(a + i) = i + 1;
}
}
0x100001:1
0x100002:2
0x100003:3
0x100004:4
0x100005:5
0x100006:
ポインタaにi足した場所にi+1を代入
a[i]と同じ意味
ポインタによる動的確保
普通の変数は固定されているが、
ポインタを使うことにより
「動的な」変数を作ることができる
int main(){
int *p = new int;
*p = 100;
delete p;
}
int型の変数の領域を新しく
作って、アドレスをpに入れる
新しく作られた領域に100を代入
newしたら必ずdelete(開放)
ポインタによる動的確保
プログラム中に常にあるのではなく、
登場したり消えたりするようなものは
ポインタを使った動的確保をするのがよい
例)ロード中にキャラクターをロード
int main(){
Pinkri *pin = new Pinkri(100, 50);
pin->Cure();
delete pin;
}
ぴんくり型のポインタ変数pinに
HP100MP50のぴんくりを生成して
そのアドレスを格納する
ポインタ変数経由でデータに
アクセスする場合は
「.」の代わりに「->」を使う
ポインタによる動的確保
一つのコード中で作ったり消したり…
int main(){
Pinkri *pin = new Pinkri(50, 30);
delete pin;
pin = new Pinkri(80, 0);
delete pin;
pin = new Pinkri(20, 60);
delete pin;
}
newしてないのにdeleteする
ということはしないように
コンストラクタは
newしたときに呼ばれ、
デストラクタは
deleteしたときに呼ばれる
配列の動的確保
動的確保で配列を作ると
要素数をプログラム中で自由に設定できる
int main(){
int *p = new int[5];
for(int i=0; i<5; i++){
p[i] = i + 1;
}
delete[] p;
}
配列をnewした場合は
deleteじゃなくて
delete[]になるのに注意
p[i]は*(p + i)と同じ意味