インスタンス

インスタンスとは

クラスの説明で述べたとおり、クラスとはオブジェクトの設計図と考えられます。
オブジェクトとしての基本的な性質を定義した設計図です。

設計図だけでは何も出来ないので、設計図から実体を作り出す必要があります。
この実体のことを「インスタンス」と呼びます。

オブジェクトの設計図が「クラス」
オブジェクトの実体は「インスタンス」
そしてインスタンスはクラスをもとに作られる。

インスタンスの生成

インスタンスの生成方法を見てみましょう。

<?php

$product = new Product();

?>

new」というキーワードが出てきました。これはクラスの実体を作り出すキーワードです。
「new クラス名()」でクラスのインスタンスが作られます。
もちろん、上記の場合であればProductという名前のクラスが事前に定義されていなければなりません。

そして作られたインスタンスは一旦、何か変数に入れて使用します。

上の例では「$product」という変数にProductクラスのインスタンスを入れました。

ここで言うと、Productクラスが商品の設計図なら、
$productは「商品そのもの」ということになります。

メンバへのアクセス

「フィールド」や「メソッド」などのクラスの構成要素を総称して「メンバ」と呼んでいます。

インスタンスは、クラスで定義されているメンバを持っています。
ここではインスタンスを使ってのメンバの使い方を見てみます。

<?php

// クラスを定義@
class Product
{
    private $name;  // 商品名
    private $price;   // 価格
    
    // 商品名を取得する
    public function getName()
    {
        return $this->name;
    }    
    
    // 商品名を設定する
    public function setName($name)
    {
        $this->name = $name;
    }    
}

// インスタンス生成
$product= new Product();

// 商品名を設定
$product->setName("かまぼこ");

// 商品名を取得
$productName = $product->getName();

var_dump($productName);

?>
string(6) "かまぼこ"

クラスで定義されているsetNameメソッドとgetNameメソッドを実行しています。
「$product->setName("かまぼこ")」でProductクラスのインスタンスの$nameフィールドに「かまぼこ」が保存されます。
その後「$product->getName()」でProductクラスのインスタンスの$nameフィールドに保存された値を取得して変数に格納しています。

インスタンスの性質

しつこいですが、クラスは設計図でインスタンスはその実体です。
ということは、設計図をもとに実体は複数作れるという考え方が出来ると思います。

車を例に考えましょう。車は自動車工場で作られますが、きっと設計図が存在するはずです。プリウスならプリウスの設計図があります。その設計図をもとに作られるプリウスは無数にありますよね。

クラスとインスタンスの関係もこれに近いと考えられます。

<?php

class Prius
{
    private $mileage = 0; // 走行距離

    // 走行する
    public function drive($distance)
    {
        // 走った距離分、走行距離を加算
        $this->mileage += $distance;
    }

    // 走行距離を取得
    public function getMileage()
    {
        return $this->mileage;
    }
}

// 2台のプリウスのインスタンスを生成
$prius1 = new Prius();
$prius2 = new Prius();

// プリウス1だけ走行してみる
$march1->drive(5);

// 両方のプリウスの走行距離を取得する
$mileage1 = $prius1->getMileage();
$mileage2 = $prius2->getMileage();

print "プリウス1は" . $mileage1 . "km走りました
"
; print "プリウス2は" . $mileage2 . "km走りました
"
; ?>
プリウス1は5km走りました
プリウス2は0km走りました

プリウスの図面が一つあります。
これをもとに2台のプリウスが生産されました。
1台目だけ5km走行しました。
その後、2台のそれぞれの現在の走行距離を見ました。
当然1台目は5kmで2台目は0kmでした。

というようなストーリーです。

ここで言いたいことは、同じクラスをもとにしていても、生成される複数のインスタンス同士は全くかかわりがないと言うことです。もちろん、クラスが同じなので性質は同じです。しかし保持しているデータはインスタンス独自のものです。
この考え方はオブジェクト指向の最大の特徴です。従来のプログラミング方式ではこういうことは出来ません。

つまりクラスの特徴は、クラスをもとにしたインスタンスはたくさん作ることができ、
その個々のインスタンスは完全に独立しているという事です。
だから同じクラスをもとに複数のインスタンスを生成しても、各インスタンスが保持している状態としてのデータはインスタンス独自であるという事です。
他のインスタンスへは一切影響を与えません。

世の中にたくさん存在するプリウスは全部同じように見えるけど、
間違いなく個々のプリウスですよね。乗る人の乗り方、整備の行き届き方などでまったく別物になっていきます。でも設計図は同じなのです。

クラスとインスタンスの関係は1対1ではないのです。

インスタンスの性質その2

これは概念的な性質というよりは、言語仕様的な話になりますが、インスタンスのもう一つの性質として、常に参照型であるということがあります。

普通は関数に変数を渡すと、関数の中でその変数の値ををどのように変化させようとも、呼び出し側には影響がありませんが、関数の呼び出しの引数に&を付けることでその変数は参照型となり、関数の中でその変数に何か変化があると呼び出し側にも変化の影響があります。つまり、前者は関数に渡すことで、関数用に変数のコピーを渡したことになり、&をつけると変数そのものを渡したことになります。ちょっと語弊があるかもしれませんが、渡す側と渡された側で同一の変数を扱っていることになるわけです。

で、実はクラスのインスタンスは常に参照型になります。関数の引数としてインスタンスを&なしで渡した場合でも、関数の中でそのインスタンスに何か変化があると、呼び出し側にも影響があることになります。

通常の変数
<?php

function sampleFunc($price)
{
    $price = 80;
}

$price = 100;
echo '初期状態:' . $price;

sampleFunc($price);
echo '値渡し後:' . $price;

sampleFunc(&$price);
echo '参照渡し後:' . $price;



?>
初期状態:100
値渡し後:100
参照渡し後:80
インスタンス
<?php

class Product
{
    private $price;

    public function setPrice($price)
    {
        $this->price = $price;
    }

    public function getPrice()
    {
        returnh $this->price;
    }
}

function sampleFunc($product)
{
    $product->setPrice(80);
}

$product = new Product();
$product->setPrice(100);
echo $product->getPrice();

// 値渡しと同様に&なしで引数を渡す
sampleFunc($product);
echo $product->getPrice();

?>
100
80

インスタンスはどうやっても参照渡しになります。
では値渡しにする方法はないのか?値渡しは出来ませんが、コピーする事はできます。
それにはcloneというキーワードを使います。

<?php

$productClone = clone $product;
if ($productClone === $product) {
    echo '同一インスタンス';
} else {
    echo '別々のインスタンス';
}

?>

ちなみに比較演算子===は、インスタンス同士の比較の場合、双方が同一のインスタンスである場合のみtrueとなります。

このようにしてcloneキーワードでコピーを作成してからそのコピーを関数に渡せば値渡しと同じようなことができます。

<?php

$product = new Product();
$product->setPrice(100);
echo $product->getPrice();

// cloneでインスタンスをコピーして引数に渡す
sampleFunc(clone $product);
echo $product->getPrice();

?>
100
100

でも実際のところ、インスタンスを値渡しなんて必要が発生することはまず無いと思います。

ただ、インスタンスの性質としては、常に参照という事は意識しておかないと思わぬ動きとなってしまう場合もあるので注意が必要です。