本カテゴリーは手続き型言語になれている人がどうやったらオブジェクト指向を理解できるのかを考えて書いている記事です。

多少の言語知識があることを前提に進めます。


C#やJava等の言語では肝となる部分です。

この概念が分からないとガベージコレクションの概念が分かりません。


そして、この概念を持っているとオブジェクト指向の理解が早まります。


さて、スタック領域は皆さん知っているはずです。

特にC言語を使っている方はお手の物のはずです。


なぜなら、


普段使っている領域


ですから。


まぁ、順を追って一つずつ解決していきましょう。

まず。ポインタの概念からゆっくりと解決していきましょう。


int i = 0;


この書式はint型のiを定義して0を入れています。(*1)

では、実際マシン内では何をしているのでしょうか?


C言語を勉強した方なら当たり前の事かもしれませんが、以下の事を行っています。


4バイト(*2)の領域を確保して0を入れます。


00000000 0000000 0000000 0000000


つまり、こういうことです。


--------------------------

ちなみに


int i = 10;



00000000 0000000 0000000 0001010


こうなります。

--------------------------


では、この領域はどこを使っているのでしょうか?

って、前置きの通り


スタック領域


を使用しているに決まってますね。


では、次に


public Sankaku{

private int tyoutenAX;

private int tyoutenAY;

private int tyoutenBX;

private int tyoutenBY

private int tyoutenCX;

private int tyoutenCY;


// 頂点Aを設定するメソッド

public void SetTyoutenA(int x, int y)

{

this.tyoutenAX = x;

this.tyoutenAY = y;

}


// 頂点Bを設定するメソッド

public void SetTyoutenB(int x, int y)

{

this.tyoutenBX = x;

this.tyoutenBY = y;

}


// 頂点Cを設定するメソッド

public void SetTyoutenC(int x, int y)

{

this.tyoutenCX = x;

this.tyoutenCY = y;

}

}

をnewするとどうなるのでしょうか?


Sankaku i = new Sankaku();


先ほどのイメージどおりですと


00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000


こうなる(*3)と思いませんか?

ところが、


01010010 01001000 10010010 01001010


こうなります。

なんだか、謎の値が設定されていますね。


一体これはなんでしょうか?

C言語で


int *i;


と、定義した時に似ていませんか?


そうです。

これはポインタアドレスを指しているのです。


01010010 01001000 10010010 01001010


番のアドレスに実際の


00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000


領域を確保していることを示しています。

そして、この領域を確保している所がヒープ領域になります。


さて、それでは前回の


public static void main()

{

// newキーワードは宣言時に指定することも可能。

Sankaku sikakuA = new Sikaku();


// データ設定

sikakuA.SetTyoutenA(100, 100);

sikakuA.SetTyoutenB(100, 50);

sikakuA.SetTyoutenC(50, 100);

((Sikaku)sikakuA).SetTyoutenD(50, 50);

}


について、考えて行きましょう。

まず、


Sankaku sikakuA = new Sikaku();


によって「Sikaku」の領域を確保します。

【スタック領域】

01010010 01001000 10010010 01001010

(値は適当です。ポインタアドレスが入っています。)


【ヒープ領域】

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000


「Sikaku」の領域がこのように確保できました。

しかし、「Sankaku」で定義しているので青いデータしかアクセスが出来ません。


次にこのようなメソッドが呼び出されます。

sikakuA.SetTyoutenA(100, 100);

sikakuA.SetTyoutenB(100, 50);

sikakuA.SetTyoutenC(50, 100);


これによって

【スタック領域】

01010010 01001000 10010010 01001010

【ヒープ領域】

00000000 00000000 00000000 01100100 00000000 00000000 00000000 01100100 00000000 00000000 00000000 01100100 00000000 00000000 00000000 00110010 00000000 00000000 00000000 00110010 00000000 00000000 00000000 01100100 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000


のようにデータが入ります。


((Sikaku)sikakuA).SetTyoutenD(50, 50);


これは、「Sankaku」を「Sikaku」にキャストすることによって「Sikaku」の領域にアクセスできるようになります。

(実際は「Sikaku」で定義されているアクセスメソッドにアクセスできるようになります。)

【スタック領域】

01010010 01001000 10010010 01001010

【ヒープ領域】

00000000 00000000 00000000 01100100 00000000 00000000 00000000 01100100 00000000 00000000 00000000 01100100 00000000 00000000 00000000 00110010 00000000 00000000 00000000 00110010 00000000 00000000 00000000 01100100 00000000 00000000 00000000 00110010 00000000 00000000 00000000 00110010

さて、何となく分かったでしょうか?

分からなくても、概念だけ覚えといてください。


さて、ここでまとめです。


・スタック領域

 プリミティブ型データ(基本型データ{int,long,double等})やクラスのポインタアドレスを確保します。

・ヒープ領域

 クラスの実態を保持します。(なお、構造体は含まれません。構造体はスタック領域に確保されます。)


この概念を覚えておくと


Sankaku A = new Sankaku();

Sankaku B = A;


B.SetTyoutenA(100,100);


と言う構文がAもBも同じ場所を指していることが用意に分かると思います。

B = A

でポインタアドレスをコピーしているため、指しているヒープ領域はAもBも同じ場所である。


ちなみに、スタック領域は領域がそのまま使えるため高速で処理ができます

ヒープ領域は必ずポインタアドレスから実態を見に行くため若干速度が落ちます


C#の場合は速度を上げるために構造体を使用してスタック領域だけで処理を行うことも出来ます。

ガベージコレクションの説明の前にポリモルフィズムの説明をしようと思います。


☆ポインタアドレスについての補足(ピンと来る人には来ると思う。)☆

ポインタアドレスと言うと分かりにくいですが、住所というとどうでしょうか?

あなたの家の住所はどこですか?

実態は住所に行けば分かるのですが、管理は住所だけをしたいという関係に似ていると思います。


郵便局で、近い住所毎に分けて処理をしているのに似ていますね。


------------------------------------

*1・・・C言語等では初期化しないで使用すると何の値が入っているかわからなくなりますが、C#やJavaの場合はコンパイルエラーになります。(メンバ変数の場合はnewするときに自動的に初期化されます。)


*2・・・C#やJavaのint型は32Bit(4Byte)変数です。C言語のLong型と同じです。


*3・・・メソッドはスタティック領域(メソッド領域)と言われる領域に確保されます。インスタンスが違ってもメソッドは同じ動作を行いますから、領域をいくつも使ったりすることはありませんのでメンバ変数分だけ領域が確保されます。

(実際はクラスのサイズやその他の情報も持っている筈ですが、ここでは割愛いたします。)