※追記:主にC言語の次にJAVAかC#を勉強したときの話です。
それ以外の言語では異なる仕様があるようです。
オブジェクト指向言語で一番最初に躓いたところ
これはかなり大昔の話になりますが、私はもともとC言語からプログラミングの世界に入りました。
オブジェクト指向言語で特徴的なのは、何といってもクラスではないかと思います。
勉強を始めた当初、私の理解では
「クラス?あ~Cで言うと構造体みたいなもんね。」
という感じでした。
いざプログラミングを始めてみると
- クラスを使う前にNewするのは何の意味があるのか?
- クラス変数を定義するのとNewするのはどう違うのか?
- クラス変数を別のクラス変数に代入した場合、なぜオブジェクトがコピーされないのか。
ということがさっぱりわかりませんでした。
今になって振り返ってみてわかっていなかったこと
なぜ上記のことがわからなかったかというと、プログラミングにおける基礎的な知識が不足していたからです。
それは、メモリ確保の概念です。
(そういう意味では当時はC言語もさほど理解できていなかったですね…)
Cであれば
struct sample {
int item1;
const char* item2;
};
int main(void){
struct sample a; //構造体変数を定義
a.item1 = 1;
a.item2 = "aaa";
}
のように構造体変数を定義した時点でプログラム上にメモリが確保され、使用することができます。
C言語において変数を定義すると、変数の内容分のメモリ領域を確保することになります。
一方、クラスの場合は
public class sample{
public int item1;
public string item2;
}
class TestClass
{
static void Main(string[] args)
{
sample a; //クラス変数を定義
a = new sample(); //インスタンスを作成
a.item1 = 1;
a.item2 = "aaa";
}
}
のように、クラスをnew(インスタンスを作成)した時点でプログラム上でメモリが確保され、初めて使用できるようになります。
インスタンスとはクラスで定義された内容を取り扱うために確保されたメモリ領域のことであり、インスタンスを作成するとはメモリ上に領域を確保することである、ということが勉強をはじめた当初はわかっていませんでした。
構造体変数は変数自体が構造体の本体であり構造体用のメモリ領域を持っていますが、クラス変数は使用できるメモリ領域を持っていません。1
クラス変数は、インスタンスを指し示しているだけです。Cで言うところのポインタのようなものです。
例えば、Cで
int main(void){
struct sample a; //構造体変数を定義
struct sample b; //構造体変数を定義
a.item1 = 1;
a.item2 = "aaa";
b.item1 = 2;
b.item2 = "bbb";
a = b;
}
としたとき、構造体変数aとbの中身は同一になりますが、それぞれa用のメモリ領域とb用のメモリ領域の2か所に同じデータを持っている状態になります。
このとき
a.item1 = 3;
b.item1 = 4;
と更新すると、それぞれ違うメモリ領域を更新し、違う値になります。(a.item1=3,b.item1=4)
一方、C#で
static void Main(string[] args)
{
sample a; //クラス変数を定義
sample b;
a = new sample(); //インスタンスを作成
a.item1 = 1;
a.item2 = "aaa";
b = new sample(); //インスタンスを作成
b.item1 = 2;
b.item2 = "bbb";
a = b;
}
としたとき、クラス変数aとbの中身は同一になりますが、元b用のメモリ領域にある1つのデータをaとbが共通して指し示している状態になります。このとき元aが指し示していたメモリ領域はどこに行くのかという話になりますが、ガベージコレクションが実装されている言語では、どの変数からも参照されなくなったメモリ領域は自動的に解放されます。
この場合
a.item1 = 3;
b.item1 = 4;
と更新すると、aもbも同じ1か所のメモリ上のデータを更新しているので両方とも同じ値になります。(a.item1=4,b.item1=4)
初心者にとって大事だと思うこと
今になって思うことはプログラムを始めて早い段階で理解しておくべきはメモリ確保に関する概念だったなぁということです。
そこをあやふやにしたまま進んでいくと、どこかで先に進めなくなるタイミングが来ると思います。
昔のオブジェクト指向言語の入門書などでは、最初は何も考えずにとりあえずオブジェクトはnewしとけみたいな感じでかなり先まで進んでしまったような気がしますが、最初にそこを追及しておけば後々苦労しなかったと思います。
まぁ、ある程度わかるようになった後だから言えることかもしれませんが…。
-
参照先を示す領域は持っているというツッコミはなしで… ↩