オブジェクト指向: プロパティ・メソッド

クラス変数、インスタンス変数

class Parent:
 # クラス変数
 class_var = “Parent class_var”

 def __init__(self):
  # インスタンス変数
  self.instance_var = “Parent instance_var”

class Child(Parent):
 # クラス変数
 class_var = “Child class_var”

 def __init__(self):
  # インスタンス変数
  self.instance_var = “Parent instance_var”

print(Parent.class_var)
→Parent class_var

print(Parent.instance_var)
→エラー

parent = Parent()
print(parent.class_var)
→Parent class_var

print(parent.instance_var)
→Parent instance_var

print(Child.class_var)
→Child class_var

print(Child.instance_var)
→エラー

child = Child()
print(child.class_var)
→Child class_var

print(child.instance_var)
→Parent instance_var

補足
pythonではインスタンス変数を明示的に定義する事はできない
コンストラクタでself.〜で定義する

注意
pythonではクラス内部からクラス変数にアクセスする場合でもself.〜と書く
この為、クラス変数と同名のインスタンス変数をコンストラクタ内で定義すると、インスタンス、クラス両方からアクセスできる別々の変数となる

プロパティ

アクセサ

メンバ変数(インスタンス変数)に外部からアクセスすつ為のメソッド
get(set)アクセサ、getter(setter)と呼ばれる。

$p = new MyClass();

$p->SetMyVal("aaa");
$val = $p->GetMyVal
$val:aaa

class MyClass{
 private $_myVal;
 
 function SetMyVal($prm)
 {
  $this->_myVal = $prm;
 }
 function GetMyVal()
 {
  return $this->_myVal;
 }
}

p = MyClass()
p.set(“aaa”)
print(p.get())
->aaa

class MyClass:
 def get():
  print(self._myVal)

 def set(prm):
  self._myVal = prm

private class clsParent{
 private int _member1;
 public int member1
 {
  //getアクセサ
  get { return this._member1; }
  //setアクセサ
  valueは宣言不要
  set { _member1 = value; }
 }

 //↑の省略形
 public int member2 { get; set; }

 public void SubRoutin1(){
  ~
 }
}

public void main()
{
 clsParent myParent = new clsParent();
 myParent.member1 = 5;
 int x = myParent.member1;

 myParent.member2 = 10;
 int y = myParent.member2;

 myParent.SubRoutin1();
}

class MyClass
{
private:
  int _member;
public:
  int GetMember();
  void SetMember(int member);
};

int MyClass::GetMember()
{
  return this->_member;
}

void MyClass::SetMember(int member)
{
  this->_member = member;
}

int main(array<System::String ^> ^args)
{
  MyClass *myClass = new MyClass();
  myClass->SetMember(10);
  Console::WriteLine(myClass->GetMember());
  // →10
  delete myClass;
  
return 0;
}

※Getter/Setterを同名でオーバーロードすると呼び出し側からは使いやすくなる。
オーバーロード」参照

Private Class clsTest()
 Dim _Menber As Integer
 
 Public Property Menber As Integer
  getアクセサ
  Get
   Return _Member
  End Get
  setアクセサ
  Set(ByVal prm As String)
   _Member = prm
  End Set
 End Property
 
 Public Sub SubRoutine()
  ~
 End Sub
End Class

Dim myTest As New clsTest()
myTest.Member = 10
Dim x As Integer = myTest.Member
Call myTest.SubRoutine()

クラスモジュール
MyClass.cls

Dim pMyVal As Integer
Dim pMyObj As Object

参照(読み取り)専用:データ型
Public Property Get Getter() As Integer
  Getter = pMyVal
End Property

参照(読み取り)専用:オブジェクト型
Public Property Get Getter() As Object
  Set Getter = pMyVal
End Property

設定(書き込み)専用:データ型
Public Property Let Letter(ByVal prm As Integer)
  pMyVal = prm
  ※正確には↓ の様に書くが「Let」は省略可能。
  Let pMyVal = prm
End Property

設定(書き込み)専用:オブジェクト型
Public Property Set Setter(ByVal prm As Object)
  Set pMyObj = prm
End Property

イニシャライザー
Private Sub Class_Initialize()
  Call MsgBox("Initialize()")
End Sub

ファイナライザー
Private Sub Class_Terminate()
  Call MsgBox("Terminate()")
End Sub

※呼び出し元
Private Sub Form_Load()

  Dim myClass As myClass
  Set myClass = New myClass
  
  myClass.Letter = 5
  Let myClass.Letter = 5
  
  Dim myObj As Object
  Set myClass.Setter = myObj
  
  Debug.Print (myClass.Getter)
  
  Set myClass = Nothing
End Sub

private class clsParent{
 private int _member1;
 public int member1
 {
  //getter
  get { return this._member1; }
  //setter
  valueは宣言不要
  set { _member1 = value; }
 }

 public void SubRoutin1(){
  ~
 }
}

public void main()
{
 clsParent myParent = new clsParent();
 myParent.member1 = 5;
 int x = myParent.member1;

 myParent.member2 = 10;
 int y = myParent.member2;

 myParent.SubRoutin1();
}

function Calc1(prm1, prm2){
 this.member1 = prm1;
 this.member2 = prm2;
}
var number = new Calc1(1, 2);
var x = number.member1 + number.member2;
⇒ x : 3

number.member1 = 10;
number.member2 = 20;
var y = number.member1 + number.member2;
⇒ x : 30

var Calc2 = function(prm1, prm2){
 this.member1 = prm1;
 this.member2 = prm2;
};

var number = new Calc2(100, 200);
var x = number.member1 + number.member2;
⇒ x : 300

number.member1 = 1000;
number.number2 = 2000;
var y = number.member1 + number.member2;
⇒ y : 3000

読み取り・書き込み専用属性
//フィールドをprivate属性にし、アクセサを読み取り用しか定義しない。
int _member;
public int member{ get; }

int _member;
public int member{ set;}

宣言時、コンストラクタでのみ設定可能なインスタンス変数
(固定値(const)と違い、インスタンス毎に値を変えられる)
public class Test
{
  private readonly int member = 5;
  public Test(int init)
  {
    this.member = init;
  }
  
  public int Member
  {
    get { return this.member; }
    ⇒ OK
    set { this.member = value; }
    ⇒ ビルドエラー
  }
}

 Dim _Menber As Integer
 Public ReadOnly Property Menber As Integer
  Get
   Return _Member
  End Get
 End Property
 
 Dim _Menber As Integer
 Public WriteOnly Property Menber As Integer
  Set(ByVal prm As String)
   _Member = prm
  End Set
 End Property
インデクサ

※インスタンス自身をプロパティとして扱う

using System.Collections.Generic;
public class MyList<T>
{
  T[] _list = new T[1000];
  public T this[int index]
  {
    get { return _list[index]; }
    set { _list[index] = value; }
  }
}

public void Form1_Load(object sender, EventArgs e)
{
  MyList<int> myList = new MyList<int>();
  
  for (var i = 0; i < 10; i++)
  {
    myList[i] = i;
    ※インスタンスをプロパティとして扱っている
  }
}

Imports System.Collections.Generic
Public Class MyList(Of T)
  Dim _list(1000) As T
  
  //デフォルトのプロパティを宣言
  Default Public Property list(ByVal index As Integer) As T
    Get
      Return _list(index)
    End Get
    Set(value As T)
      _list(index) = value
    End Set
  End Property
  
End Class

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
  
  Dim myList As New MyList(Of Integer)
  
  For i As Integer = 0 To 10 Step 1
    myList(i) = i
    ※インスタンスをプロパティとして扱っている
  Next i
  
End Sub

メソッド

メソッド種類
メソッド種類 インスタンスからのアクセス クラスからのアクセス
インスタンスメソッド
スタティックメソッド
クラスメソッド
言語毎のメソッド有無
言語 インスタンスメソッド スタティックメソッド クラスメソッド
Java
PHP
Python
スタティックメソッド、クラスメソッドの違い

言語によっては同じだったり片方が存在しなかったりする

pythonの場合
クラスに依存しないので単なるサブルーチンとして名前空間を分ける為に使用する

クラス内サブルーチン
public宣言する事でクラス外部から呼ばせる事が可能。

public class Parent
{
 private int member;
 public void SubRoutine(int prm1, int prm2)
 { this.member = prm1 + prm2; }
}

public void main()
{
  Parent p = new Parent();
  p.SubRoutine(prm1:5, prm2:10);
}

// インライン定義
class MyClass
{
private:
  int ~;
public:
  void sayMember(){
    ~
  }
};

// 外部宣言
class MyClass
{
private:
  int ~;
public:
  // プロトタイプ宣言
  void sayMember();
};

void MyClass::sayMember()
{
  ~;
}

int main(array<System::String ^> ^args)
{
  インスタンスをスタック領域に作成
  MyClass myClass
  myClass.sayMember();
  
  インスタンスをヒープ領域に作成
  MyClass *myClass = new MyClass();
  myClass->sayMember();
  delete myClass
  
  return 0;
}

※スタック領域/ヒープ領域については、「メモリ管理/ヒープ領域」参照

Public Class Parent
 Private member As Integer
 Public Sub SubRoutine(
   ByVal prm1 As Integer, _
   ByVal prm2 As Integer)
   me.member = prm1 + prm2
 End Sub
End Class

Public Sub Main()
  Dim p As New Parent()
  p.SubRoutine(prm1:=5, prm2:=10)
End Sub

C#と同じ
var Parent = function(){
  this.answer;
  this.add = function(prm1, prm2){
    this.answer = prm1 + prm2;
  };
};
$p = new MyClass();

$p->Method();
aaa

class MyClass{
 function Method(){
  print("aaa");
 }
}

オーバーロード

多重定義
引数・戻り値の異なる同名のメソッドを定義する事。
似た機能を同名にする事で直感的に使いやすくなる。
設計時には名前衝突も起こり難い。

private class Parent
{
 public int member{ get; set; }
 
 public void SubRoutine(int prm1, int prm2)
 { this.member = prm1 + prm2; }
 
 //オーバーロード(引数が違う)
 public void SubRoutine()
 { Console.WriteLine(this.member); }
 
 //オーバーロード(戻り値が違う)
 ※引数も変える必要あり
 public int SubRoutine(int prm1, int prm2, int prm3)
 {
  return prm1 + prm2 + prm3;
 }
}
※C++はクラス内に限らず通常メソッドでもオーバーロード可能

※Getter/Setterを同名でオーバーロードした例
class MyClass
{
private:
  int _member;
public:
  int Member();
  void Member(int member);
};

int MyClass::Member()
{
  return this->_member;
}

void MyClass::Member(int member)
{
  this->_member = member;
}

int main(array<System::String ^> ^args)
{
  MyClass *myClass = new MyClass();
  myClass->Member(10);
  Console::WriteLine(myClass->Member());
  // →10
  delete myClass;

  return 0;
}

Public Class Parent
 Private member As Integer
 Public Sub SubRoutine(
   ByVal prm1 As Integer, _
   ByVal prm2 As Integer)
   me.member = prm1 + prm2
 End Sub
 
 オーバーロード(引数が違う)
 Public Overloads Sub SubRoutine()
  Console.WriteLine(me.member)
 End Sub
 
 オーバーロード(引数が違う)
 ※引数も変える必要あり
 Public Overloads Function SubRoutine(
   ByVal prm1 As Integer, _
   ByVal prm2 As Integer, _
   ByVal prm3 As Integer) As Integer
  Return prm1 + prm2 + prm3
 End Sub
End Class
private class Parent
{
 private int member;
 
 public void SubRoutine(int prm1, int prm2)
 { this.member = prm1 + prm2; }
 
 //オーバーロード(引数が違う)
 public void SubRoutine()
 { Console.WriteLine(this.member); }
 
 //オーバーロード(戻り値が違う)
 ※引数も変える必要あり
 public int SubRoutine(int prm1, int prm2, int prm3)
 {
  return prm1 + prm2 + prm3;
 }
}
var Parent = function(){
  this.answer;
  this.add = function(prm1, prm2){
    this.answer = prm1 + prm2;
  };
};

var Child = function(){};

//継承
Child.prototype = new Parent();
Child.prototype.minus = function(prm){
  this.answer -= prm;
};

var child = new Child();
child.add(1,1);
child.minus(1);
⇒this.answer = 1

演算子のオーバーロード
class MyClass
{
 int x, y;
 
 public MyClass() { x = y = 0; }
 public MyClass(int i, int j) { x = i; y = j; }
 
 public static MyClass operator +(MyClass prm1, MyClass prm2)
 {
  MyClass result = new MyClass();
  
  result.x = prm1.x + prm2.x;
  result.y = prm1.y + prm2.y;
  
  return result;
 }

}

class Program
{
 static void Main(string[] args)
 {

  MyClass a = new MyClass(1, 1);
  MyClass b = new MyClass(10, 10);
  MyClass c = new MyClass();
  c = a + b;
  c.x:11 c.y:11
  +はMyClassとMyClassの値が足されている
 }
}

class MyClass
{
 int x, y;
 
 public MyClass() { x = y = 0; }
 public MyClass(int i, int j) { x = i; y = j; }
 
 public static MyClass operator +(MyClass prmObj, int prmInt)
 {
  MyClass result = new MyClass();
  
  result.x = prmObj.x + prmInt;
  result.y = prmObj.y + prmInt;
  
  return result;
 }
}

class Program
{
 static void Main(string[] args)
 {

  MyClass a = new MyClass(1, 1);
  MyClass result = new MyClass();
  
  result = a + 5;
  result.x:6 result.y:6
  +はMyClassとintの値が足されている
 }
}

class MyClass
{
private:
 int myInt1;
 int myInt2;
public:
 MyClass(int prm1, int prm2);
 void Show();
 MyClass operator+(MyClass obj);
};

MyClass::MyClass(int prm1 = 0, int prm2 = 0)
{
 this->myInt1 = prm1;
 this->myInt2 = prm2;
};

void MyClass::Show()
{
 cout << "myInt1:" << this->myInt1 << endl;  cout << "myInt2:" << this->myInt2 << endl; } MyClass MyClass::operator+(MyClass obj)
{
 MyClass m;
 m.myInt1 = this->myInt1 + obj.myInt1;
 b.myInt1 + c.myInt2
 m.myInt2 = this->myInt2 + obj.myInt2;
 b.myInt2 + c.myInt2

 return m;
 結果をa.myInt1、a.myInt2に格納
 インスタンス:mはここで破棄される
}

int _tmain(int argc, _TCHAR* argv[])
{
 MyClass a(0, 0);
 MyClass b(10, 20);
 MyClass c(50, 60);

 a = b + c;
 オーバーロードした+を利用
 bの+関数へcを渡し、結果をaに格納する
 a = b.+(c)

 a.Show();
 →myInt1:60 myInt2:80

 return 0;
}

クラス外にオーバーロード演算子を定義
パターン1
class MyClass
{
private:
 int myInt;
public:
 MyClass(int prm){ this->myInt = prm; };
 int Getter(){ return this->myInt; }
};

通常の+演算子をオーバーロード
MyClass operator+(int prmI, MyClass prmM)
{
 return MyClass(prmI + prmM.Getter());
}

int _tmain(int argc, _TCHAR* argv[])
{

 MyClass a(0);
 MyClass b(100);
 
 a = 1000 + b;
 オーバーロード+に引数1000とbインスタンスを渡し、結果をaに格納
 (なぜか)左側数値が第一引数、右側オブジェクトが第二引数
 →a.myInt:1100

 return 0;
}

パターン2
MyClass operator+(MyClass m1, MyClass m2)
{
 return MyClass(m1.Getter() + m2.Getter());
}

int _tmain(int argc, _TCHAR* argv[])
{

 MyClass a(0);
 MyClass b(100);
 MyClass c(100);
 
 a = b + c;
 オーバーロード+に引数:aインスタンスとbインスタンスを渡し、結果をaに格納
 (なぜか)左側数値が第一引数、右側オブジェクトが第二引数
 →a.myInt:200

 
 return 0;
}

メンバーの追加(JS)
//定義済みクラスに対してメンバー追加
var MyClass = function(){};

MyClass = function(){
 this.member1;
};
MyClass = function(){
 this.add1 = function(){
  this.member1 += 1;
 };
};

var myclass = new MyClass();
myclass.member1 = 5;
myclass.add1();
// ⇒ myclass.member1 = 6;

//インスタンスに対してメンバー追加
member.Minus = function(){
 return this.member1 – this.member2;
};
number.Minus();
⇒-1000;

var newMember = new Calc2();
newMember.Minus();
//↑ 未定義エラー

インスタンス作成時のメソッドのコピー
※プロパティはインスタンス毎に異なる値を持たせる意味はあるが、
 全く同じ挙動をするメソッドを全インスタンスにコピーするのはメモリの無駄。
 その為、例えばWebシステム用言語JavaScriptではprototype等を用いてメソッドのインスタンスへのコピーを省略する仕様を持つ。
 しかしC#ではメソッドはインスタンスにコピーされず、クラスを参照する為のポインタがコピーされるのみ。
 クラスのコピー(インスタンス作成)の際の、メソッドに関するメモリ制御を考慮する必要はない。
※C#と同様。
//プロトタイプ(インスタンス時にメモリにコピーされないメンバー定義)
※プロパティはインスタンス毎に異なる値を持たせる意味はあるが、
 全く同じ挙動をするメソッドを全インスタンスにコピーするのはメモリの無駄。
 ブラウザの読み込み毎にインスタンスを作成するWEBシステムの特性を考慮し、
 メソッドについては通常、以下の機能を用いてメモリーコピーを行わせない。

MyClass.prototype.plus10 = function(number){
 this.member1 += 10;
 this.member2 += 10;
}
var myclass = new MyClass()
myclass.plus10()

//プロトタイプ定義の別の書き方
MyClass.prototype = {
 this.plus10 = function(number){
  this.member1 += 10;
  this.member2 += 10;
 }
}