Swift 構造体とクラスの違い(初心者向け)
構造体は値型, クラスは参照型
つまり、構造体のインスタンスは値そのものを保持するのに対し、クラスのインスタンスは値が入っているメモリへの参照を保持します。 これらの違いは実際にプログラムを実行すると分かりやすいので確かめてみましょう。
まずは構造体から
struct Pokemon { var type: String var hp: Int var attack: Int var defense: Int } var pikatyu = Pokemon(type: "Electrical", hp: 100, attack: 60, defense: 40) var metamon = Pokemon(type: "Normal", hp: 80, attack: 50,defense: 50) metamon = pikatyu metamon.attack = 100 print("metamonAttack = ", metamon.attack) print("pikatyuAttack = ", pikatyu.attack)
実行結果
metamonAttack = 100 pikatyuAttack = 60
実行結果は上のようにmetamon
のattack
は100に更新され、pikatyu
は60のままです。まあ、自然な挙動ですね。
ではクラスではどうでしょうか?
クラスで同様のコードを実行した時の挙動を示します。(ちなみに下のコードのinit()
ように、クラスでは非オプショナル型プロパティは、宣言時に値を設定するか、イニシャライザの中で初期値を与える必要があります。)
class Pokemon { var type: String var hp: Int var attack: Int var defense: Int init(type: String, hp: Int, attack: Int, defense: Int){ self.type = type self.hp = hp self.attack = attack self.defense = defense } } var pikatyu = Pokemon(type: "Electrical", hp: 100, attack: 60, defense: 40) var metamon = Pokemon(type: "Normal", hp: 80, attack: 50,defense: 50) metamon = pikatyu metamon.attack = 100 print("metamonAttack = ", metamon.attack) print("pikatyuAttack = ", pikatyu.attack)
実行結果
metamonAttack = 100 pikatyuAttack = 100
結果はmetamon
とpikatyu
双方のattackが100となりました。もしmetamon
のattack
だけ更新したかったのなら、これは意図した状態とは違いますね。
クラスでは、metamon
とpikatyu
は同じメモリへの参照を保持しているので、metamon.attack = 100
は双方の指しているメモリの中にあるattack
プロパティの値を書き換える命令となり、pikatyu.attack
も100という値を返すようになります。
(補足) 当然ですが下のコードように、pikatyu
とmetamon
を別のインスタンスとして生成した場合には、同じPokemonクラスのインスタンスとはいえ、値は別物です。それぞれ値を格納するメモリは別のものが用意されます。ただ、上で見てきたような他のインスタンスをコピー(関数の引数なども含む)して生成したインスタンスと混在したり、処理が複雑になった場合には、混乱を招きそうですね。
class Pokemon { var type: String var hp: Int var attack: Int var defense: Int init(type: String, hp: Int, attack: Int, defense: Int){ self.type = type self.hp = hp self.attack = attack self.defense = defense } } var pikatyu = Pokemon(type: "Electrical", hp: 100, attack: 60, defense: 40) let metamon = Pokemon(type: "Normal", hp: 100, attack: 60, defense: 40) metamon.attack = 100 print("metamonAttack = ", metamon.attack) print("pikatyuAttack = ", pikatyu.attack)
metamonAttack = 100 pikatyuAttack = 60
これまで見てきたように、構造体のインスタンスは明示的に変更しない限りプロパティの値は変更されませんので、より安全な方法と言えます。その一方で、クラスではインスタンスがメモリへの参照のみ保持するので、ある値を複数のインスタンス同士で共有したいなどの事情がある場合はクラスを使うと良さそうです。