第2章 オブジェクト指向の基礎 第2.3節...
Transcript of 第2章 オブジェクト指向の基礎 第2.3節...
第2章 オブジェクト指向の基礎 第2.3節 メンバ変数とインスタンス
インスタンスはクラスにおいてすごく重要な概念です。
そのインスタンスとメンバ変数のさらなる詳しい部分について触れていきます。
1 第2.3節 メンバ変数とインスタンス
静的メンバ変数(1)
静的メンバ変数とはインスタンスの数に関係なく、メモリ上に一つのみ作成されるメンバ変数
文法 static データ型 変数名;
文法 クラス名::変数名;
宣言
参照
静的メンバ変数の初期化は普通の変数とは異なります
文法 データ型 クラス名::変数名 = 初期値;
これをクラスの外に書こう!
2 第2.3節 メンバ変数とインスタンス
静的メンバ変数(2)
#include <stdio.h> class myClass{ public: myClass(void); static int count; }; myClass::myClass(void){ myClass::count++; } int myClass::count = 0; int main(void){ for(int i = 0; i < 5; i++){ myClass a1; } printf("%d¥n", myClass::count); return 0; }
次のプログラムを実行しよう
countメンバ変数はaクラスの インスタンスが作成された回数を 保存しています
3 第2.3節 メンバ変数とインスタンス
静的メンバ変数(3)
とあるクラスに特化したグローバル変数として使える
►インスタンスの作成回数等、
各インスタンスの総合的な数値等の保存
►各インスタンス全体の設定等
ただし、あまり静的メンバ変数を使う機会はありません・・・
静的メンバ変数、静的メンバ関数を合わせて、 静的メンバと呼ぶことがあります
4 第2.3節 メンバ変数とインスタンス
インスタンスへのポインタ(1) 次のコードはエラーになります。その原因は何でしょうか。
class point{ public: int x ,y; }; int main(void){ point location; point* plocation; plocation = &location; *plocation.x = 5; return 0; }
*plocation.x = 5; 問題のコードはこれです
何が問題なのでしょうか?
5 第2.3節 メンバ変数とインスタンス
インスタンスへのポインタ(2) *plocation.x = 5;
実はこのコードは演算子の優先順位に問題があります
演算子 意味 優先順位 種類
. メンバアクセス 2 二項
* 間接参照 3 単項
上の文にこの優先順位に従って括弧を付けると 以下の通りになります
*(plocation.x) = 5;
plocationはインスタンスではなく、 ポインタなのでドット演算子は使えません!
ドット演算子を使いたいのはplocationが指してる先ですよね!
6 第2.3節 メンバ変数とインスタンス
インスタンスへのポインタ(3) よって以下のように修正すれば、正常に動きます
*plocation.x = 5; (*plocation).x = 5;
いちいち括弧を付けるのはめんどくさいですよね。 そこで、アロー演算子というのが用意されています。
アロー演算子は、ポインタの先のインスタンスのメンバに アクセスするのに使います。
演算子 意味 優先順位 種類
-> ポインタの先の メンバアクセス
2 二項
(*a).b
a->b つまり、右の二つは同じ意味
7 第2.3節 メンバ変数とインスタンス
thisポインタ(1) メンバ関数内でthisと書くことで、 自分自身のインスタンスへのポインタを取得できます。
class point{ public: int x ,y; point(void); }; point::point(void){ //thisを使ってメンバ変数にアクセス
this->x = 0; this->y = 0; }
ただし、この「this->」は省略することが可能です。 今まではずっとこれを省略して書いてきました。
8 第2.3節 メンバ変数とインスタンス
thisポインタ(2) thisはメンバ変数とローカル変数を区別するために 使われることがあります。
class point{ public:
int x ,y;
point(int x,int y); }; point::point(int x ,int y){
this->x = x; this->y = y;
}
ローカル変数とメンバ変数の両方に同名の変数があった場合
「this->」を付ければメンバ変数、
省略すればローカル変数になります。
thisポインタは 静的メンバ変数内では
使用不可
9 第2.3節 メンバ変数とインスタンス
インスタンスのコピー(1)
#include <stdio.h> class myClass{ public: int n; int* pn; myClass(void); }; myClass::myClass(void){ pn = &n; } int main(void){ myClass a1,a2; a1 = a2; a1->pn = 5; printf("%d¥n", a2.n); return 0; }
次のプログラムを実行しよう
代入演算子を用いれば、 インスタンスのコピーが 作れるのですが・・・
コピー後、 a2とは無関係そうなa1のメンバの ポインタの先を編集すると a2まで影響を受けてます
ではなぜでしょうか?
10 第2.3節 メンバ変数とインスタンス
インスタンスのコピー(2) main関数を一文ずつ見ていきましょう
myClass a1 ,a2; ・・・aクラスのインスタンスa1,a2ができる
a1 a2
int* pn; int* pn; int n; int n;
a1 = a2;
a1 a2
int* pn; int* pn; int n; int n;
・・・a2の内容をa1にコピー
11 第2.3節 メンバ変数とインスタンス
インスタンスのコピー(3) *(a1.pn) = 5; ・・・a1のpnの指している先に
5を代入する
a1 a2 5
int* pn; int* pn; int n; int n;
printf("%d¥n", a2.n);
a1 a2 5
int* pn; int* pn; int n; int n;
・・・a2のnの内容を表示
12 第2.3節 メンバ変数とインスタンス
インスタンスのコピー(4) インスタンスをコピーする際にポインタ変数までコピーされる →ポインタ変数が本来意図しない場所を指す
解決策
インスタンスのコピーを極力行わない!!
「いやいや、解決してないじゃん」と思うかも知れませんが、 VB.NET等インスタンスのコピーができない言語もあるので
大丈夫!!
どうしてもこの問題を解決したい場合は、 コピーコンストラクタという物を使えば解決できますが、 この講習では触れません。
13 第2.3節 メンバ変数とインスタンス
インスタンスのコピー(5)
クラスのインスタンスを関数の引数や戻り値として 利用したい場合、どうコピーを回避しましょうか?
ここで登場するのがポインタです!
引数や戻り値にインスタンスのアドレスを渡してやれば、 インスタンスそのもののコピーを回避することができます
void func(a* a1){ /* 何らかの処理 */
} int main(void){ a a1; func(&a1); return 0; }
ポインタと関数について 詳しくはC言語講習 「第6章 ポインタ」で
14 第2.3節 メンバ変数とインスタンス
インスタンスの動的確保(1)
配列と同じく、クラスのインスタンスも動的確保を行うことが出来ます
文法 new クラス名(コンストラクタの引数);
これでインスタンスへのポインタを取得できます
動的配列と同様にメモリの開放をする必要があります
文法 delete インスタンスへのポインタ;
15 第2.3節 メンバ変数とインスタンス
インスタンスの動的確保(2)
主に、後で説明する継承のときに使うよ
意図的に関数中でインスタンスの作成をしたいとき
インスタンスのコピーを避けるため
動的確保をする目的
言葉で説明するのは難しいので、 実習で具体的なコードに触れていこう
16 第2.3節 メンバ変数とインスタンス