Edited at

わい、static変数とstaticメソッドについて熱く語る


はじめに

昨今、プログラマーと会話をしていると

staticをつけた変数はクラス内で共有されるとか

staticをつけたメソッドはどこからでも呼べるとか

そのくらいの理解はしているけど、結局staticってよくわかんないんだよね

というプログラマーがちょくちょくいるなぁと思ったので

徹底的に解説してみよう!

と意気込んだものの

徹底的にとなるとめっちゃボリュームあって書くの疲れちゃうな

と思ったので

なんとなく暗記で覚えてる人が

もう、ちょっと視点を変えてstaticを捉えられる

くらいの雑な記事を書いてみた次第です。


対象読者

ある程度プログラムかけるようになってきたけど

初心者に毛が生えたくらいのレベルだなと感じている人

staticは使うし使えるけど、どこかモヤっとしている人


ちな経歴

最初はWEBから入ってHTML、CSS、JS、PHPを3~4年

並行してVBとC#もやってました。(ASP.NETで結局WEB)

プログラム歴4年くらいのころに

WEBは作れるけどゲームとかどうやって作るのか検討もつかない

と思いゲーム系の専門学生へ身を転じ、そのままゲーム業界へ

ゲーム開発は基本C++で、合計5年くらいC++をやってました。

幸か不幸かミリオンタイトルのリーダー兼リードプログラマーに任命され

恥ずかしながらちょっとドヤっていた時期もありました。

その後ゲーム開発にも飽き、フリーランス→講師→ベンチャーなどをへて

現在は最近流行りの引きこもりをやっています。

話がそれたので戻します。


「staticってそういうもんだったのか!」ってなった時期

プログラム歴6年とか、そのくらいの時期だったかなと思います。

長い事「staticはよくわからんけど使えりゃいいんでしょ」

状態で過ごしていました。

単純に知ろうとしなかったし、深く考えもしなかったので

知るのに6年もかけてしまったという悲しい現実です。


この記事の方針

この記事では

「staticをつけるとどうなるのか?」

という視点ではなく

「なんでstaticとかいうものがあるの?」

という視点で書いていきたいと思います


謝辞


サンプルコードについて

説明のために、少々PHPのコードを載せていますが

あくまでイメージのためのコードなので

「そんな書き方おかしいだろ」という点があっても気にしないようにしてください。


もろもろ簡略化しています

理解のしやすさ&記事を書くのが大変という理由から簡略化している部分が多分にあります。

細かいことを突っ込むと、正確ではないという部分もあるかと思いますが

ご容赦ください。

では本題に入ります。


static変数

こんなクラスがあったとします

<?php

class A
{
public $foo = 1;
public $var = 1;
}
?>

クラスを書いたら、基本はnewして使います。

$a = new A();

「クラスをnewする」というのは

言い換えると

「必要な分だけメモリを確保する」

ということになります。

「new = メモリの確保」です。

以下の絵はメモリです。

データが10個しか入らない超しょぼいメモリです。

<?php

class A
{
public $foo = 1;
public $var = 1;
}
?>

前述した、このクラスAには$foo$varという

2つの変数(データ)があります。

少し言い換えると

「このクラスはデータ2つ分のメモリが必要です。」

という情報が書かれているとも言えます。

事実として、new A()をすると

データ2つ分のメモリが確保されます。

メモリはこうなります。



  • 目印の列はわかりやすさのために書いていますが、実際のメモリに目印というものはありません。

  • 実際のメモリにあるのはメモリの位置を表す番号(アドレス)データのみ。

さて、クラスAをnewすることで、10個のうち2つが使われてしましました。

使えるのは残り8個です。


少し脱線した話

ちなみに今回newによって確保されたこのメモリですが

このままでは解放されてしまいます。

いわゆるGC(ガベコレ)です。

話を戻します。


先ほど、「new = メモリの確保」と書きましたが

もう1つやってくれる事があります。

それは

「確保したメモリの先頭の番号を教えてくれる」ということです。

コードを例に解説します。

$a = new A();

このコードを補足すると、こんな具合です。

そしてメモリはこうなります。

$aも変数(ローカル変数)です。

変数は例外なくメモリを使いますので

この1行の処理で3つもメモリを消費してしまいました。


豆知識::ちょうどいいので参照という表現について解説

$aという変数には「newで確保したメモリの番号」

つまり「データの存在する場所」が記憶されています。

$a->foo

このコードを噛み砕くと

$a(メモリ番号1)にあるfooに該当するデータ」

ということになります。

「クラスは参照型だ」とよくいいますが

メモリの番号をもとに、実際のデータを見に行く(参照しにいく)ので

参照型とか参照という表現が使われています。

話を戻します。


先ほどnew A()として、クラスAをnewしましたが

続けてもう一回newしてみます。

$a = new A();

$b = new A(); // さらにnewする

この時、メモリはこのようになっています。

最初にnewした際に、メモリ番号1~3を確保しているので

2回目のnewでは、まだ確保されていないメモリ番号4~5を確保します。

そして

new「メモリ番号4から確保したよー」

$b「おっけー、4だねー」

ということで、また3つのメモリが消費されました。

もう残り4つしかありません。


豆知識:クラスは設計図、インスタンスは実態と言われる理由

クラスは設計図、インスタンスは実態と言われたりしますが

クラスは

「クラスをnewするときにどれくらいメモリが必要になるの?」

といった情報が書かれているものに該当します。

インスタンスは

クラスに書かれた情報をもとに確保したメモリに該当します。

という感じなので


  • クラス=様々な情報=設計図

  • インスタンス=実際に確保したメモリ=実態

という表現が使われています。

はい、また話を戻します。


豆知識話で話が切れてしまいましたが

ひとまず、この時点で押さえておきたい重要なポイントは


  • クラスをnewするとメモリが消費されていく

  • というより消費されてしまう

という事です。

インスタンスごとに変数(メモリ)が必要であれば

これで別に問題ないのですが

「クラスに定数(変化させないデータ)を持たせたい!」となった場合

このままではnewするたびに、同じデータなのにnewした分だけメモリが使われてしまいます。

これはおいしくないですね。

またnewするたびに、それぞれにメモリを確保してしまうと

「クラスで同じ変数(データ)を共有したい!」

となったときもできなくて困ってしまいます。

(ただのグローバル変数を使うとか代替手段はありますが)

...

プログラマーたちは思いました

「この変数は1個だけでいい。。。」

「この変数はnewした時にメモリを確保してほしくない」

「この変数は何もして欲しくない。。。」

「この変数はほっておいてくれ。。。」

「この変数は静かにしておいてくれ。。。」

「この変数は何事もなく、そっとしておいてほしい。。。」

突然ですがここで英単語の勉強です。

staticという単語ですが、これにはこんな意味があります。


static

静的、物事の状態に変化がないさま


なんと

まさしく先ほどまでプログラマーが望んでいたことではありませんか

実際にstatic変数を定義したクラスをnewする様子をみてみましょう。

<?php

class A
{
public static $foo = 1; // fooをstaticにしました。
public $var = 1;
}
?>

$a = new A();

$b = new A();

この処理を実行したとき、メモリはどうなっているでしょうか

こうなります。

いままで、クラスに定義した変数はnewすることでメモリが確保されていました。

逆に言えば

newしない限り、その変数はメモリ上に存在しないので当然アクセスできません。

さらにメモリのどこに確保されるのかも、newしてみないとわかりません。

newが、メモリを確保して、さらに「確保した場所はここだよ!」

と教えてくれて初めてデータにアクセスすることができます。

それに対して、staticがついた変数は

基本的にプログラムが実行されるタイミングで

あらかじめメモリが確保されています。

一度確保したら、プログラムが終了されるまでずっと確保されたままです。

場所も固定されています。

つまりstatic変数は最初からメモリが確保されているし

メモリのどこにあるかもわかっている変数ということです。

(ぶっちゃけグローバル変数と大差ない)

なのでstaticな変数はいつでも、どこからでもアクセスできるわけです。


static変数のまとめ

staticというのは、なんとなく変数を共通で使えるようにするもの

というイメージを持っているかもしれません

(というか自分がそう思っていましたし、事実そういう側面もあります)

しかしプログラムを知るにつれて

staticというのは、変数を共通化する魔法ではなく

変数のためのメモリを、いつ、どこに確保するのか?

を制御する魔法なのだと視点が変わりました。

些細な事かもしれませんが

こういった視点を持てるようになってからは

プログラマーとしての実力も伸びていったように感じています。

「でも、結局使い道としては、共通で使いたい変数にstaticをつけるだけじゃん?」

と思ってしまったかもしれない人に向けて

(昔の自分は思いました)

staticがついた変数と、ついていない変数では

メモリが確保されるタイミングや、メモリのデータが初期化されるタイミングも変わってきます。

このタイミングを利用した

Lazy Loadingとか遅延初期化といわれる

テクニックがあったりもします。(並列処理でよく見る)

staticって奥が深いなぁと思った出来事でした。


staticメソッド

さて、では今度はstaticメソッドについてといきたいところですが

static変数の内容だけで結構満身創痍です。

ボリューミーな内容はまた元気が出た時にということで

staticメソッドはさらっとまとめたいと思います。

わかりにくかったらごめんなさい。


staticメソッドっていっても結局はただの関数

こんな関数があったとして、基本的に関数はどこからでも実行できますよね?

<?php

function foo($a)
{
print_r($a);
}
?>

関数というのは関数名と引数さえ合っていれば呼び出せる。

そんなの当たり前だと思うかもしれませんが

クラスのメソッドとなると、なぜか関数と違うものと捉えてしまうのが人の脳。

関数もクラスメソッドも基本的には関数である

という視点を持つと視界が開けるかもしれません。


普通のメソッド

<?php

class A {
public function foo() {
print_r($this);
}
}
?>

さて、このfoo関数の中で$thisという変数を使っています。

PHPerならおなじみの$thisですね。

しかし不思議な事にこの$this

メンバ変数にも、引数にも、ローカル変数にも$thisなんて変数は定義されていません。

というより$thisという変数を定義した経験がありません。

でも$thisという変数は使えます。

つまり、誰かが$thisという変数を用意してくれている

$thisが使えるように計らってくれたやつがいる

という事実があるということです。


どうやったら$thisを使えそう?

クラスAのfooメソッドを呼ぶ処理はこう書くけど

$a = new A();

$a->foo();

こんな風に誰かがこっそり書き換えていたとしたら

$a = new A();

// ①fooの第一引数に$a渡すように書き換えてまえ
// $a->foo();
// ↓こうしてもいけるやろ
A::foo($a);

class A {
// ②ほんとは引数ないけど、こっそり第一引数を追加したろ
public function foo($this) {
print_r($this);
}
}

クラスのメソッドと普通の関数の違いは

処理の中で$thisを使えるか、使えないかだけである。

逆に言えば、「$thisが使えるということ以外は普通の関数」と同じである。

プログラムのソースコードというのはあくまでテキストファイルでしかない

実際に動作させるには、パソコンで動作する形に変化させる必要がある。

俗にいうコンパイルというやつでございます。。

プログラマはソースコードを書くことしかできないが

コンパイラはそのソースコードを都合よく書き換えることができる

プログラマは$thisなんて変数を定義してないけど

コンパイラが$thisという変数をこっそり定義しちゃうことはできるのだ。

勝手なことしてくれるものだ。

そもそも処理の中で$thisを使わないのであれば

そんな勝手なことをしてくれなくてもいいのに

何もしてくれなくていいのに。。。

そのままでいいのに。。。

突然ですがここで英単語の勉強です。

staticという単語ですが、これにはこんな意味があります。


static

静的、物事の状態に変化がないさま


なんと

まさしく先ほどまでプログラマーが望んでいたことではありませんか

つまりメソッドにstaticをつける=そのメソッドはそのままでいい

$thisなんて渡さなくていいんだ!

という事になるわけです。

$thisを渡さないってことは

当然、処理のなかで$thisなんて変数は使えませんよね?

staticメソッド中で$thisは使えませんよね?


staticメソッドまとめ

かなり駆け足で雑になってしまいましたが

何かしらの視点の変化があれば幸いです。


まとめ

最初に申し上げた通り雑な仕上がりとなってしまいました。

やはりこういった内容を文章だけで伝えようと思うと

非常に書くのも考えるのもしんどく

途中であぁもう書くのやめよってなることもしばしば

今回は雑ながらも最後まで書き切れたので多少はマシですが。。。

昨今プログラマーという職業が注目され

独学でも学べるサービスというのもたくさん溢れてきましたが

やっぱり記事より、直接教える方が楽だわーと思いました。まる。