オブジェクト指向: コンストラクタ

コンストラクタ

通常

インスタンス作成時に呼び出されるメソッド

private class clsTest
{
 public int member { get; set; }

 //コンストラクタ(クラス名と同じ)
 public clsTest(int init)
 {
  this.member = init;
 }

 public void calc()
 { this.member += 10; }

}

public void main()
{
 clsTest myClass = new clsTest(5);
 myClass.calc();
 ⇒15
}

コンストラクタのオーバーロード(多重定義)も可能
(オブジェクト指向: プロパティ・メソッド/オーバーロード参照)

コンストラクタのあるクラスを継承した場合
継承時のコンストラクタ」参照

class MyClass
{
private:
  ~;
public:
  MyClass();
};

MyClass::MyClass()
{
  ※ここが処理される
  ~
};

int main(array<System::String ^> ^args)
{
  MyClass *myClass = new MyClass();
  myClass->~;
  delete myClass;
  
  return 0;
}

Private Class clsTest
 Private _member As Integer

 ’コンストラクタ
 Public Sub New(ByVal init As Integer)
  me._member = init
 End Sub

 Public Sub Calc()
  _member += 5
 End Sub

End Class

Sub main()
 Dim myTest As New clsTest(5)
 Call myTest.Calc()
 ⇒10
End Sub

コンストラクタのあるクラスを継承した場合
継承時のコンストラクタ」参照

class MyClass{
  int member;
  
  //コンストラクタ
  MyClass(){
    this.member = 0;
  }
  
  //コンストラクタのオーバーロード
  MyClass(int prm){
    this.member = prm;
  }
}

public static void TEST( ~ ){
  MyClass myclass = new MyClass();
  ⇒myclass.member : 0;
  
  MyClass myclass = new MyClass(10);
  ⇒myclass.member : 10;
}

function Calc1(prm1, prm2){
 //コンストラクタ
 this.member1 = prm1;
 this.member2 = prm2;
 this.Add = function(){
  return this.member1 + this.member2;
 };
}
var number = new Calc1(1, 2);
var x = number.Add();
⇒x = 3

$p = new MyClass("aaa");
$p->Method();
->aaa

class MyClass{
 private $_myVal;
 
 function __construct($prm)
 {
  $this->SetMyVal($prm);
 }
 
 function SetMyVal($prm)
 {
  $this->_myVal = $prm;
 }
 
 function Method(){   
  print($this->_myVal);
 }
}

デフォルトコンストラクタ

コンストラクタを定義しなかった場合に、
コンパイラによって自動的に作成されるコンストラクタ
引数及び処理内容無し。

同一クラス内からのコンストラクタ呼び出し

引数有りコンストラクタやメソッドからデフォルトコンストラクタを呼び出す事は出来ない。
共通の処理をprivateのメソッドとして定義しておき、それを呼び出す方法がある。

class MyClass{
  // ①
  public MyClass(){
    ~
  }
  // ②
  public MyClass(int prm){
    ※引数無しコンストラクタ①の呼び出し
    this();
  }
}

public class Base {
  public static void main(String[] args) {
    ※引数有りコンストラクタ②の呼び出し
    MyClass m = new MyClass(5);
  }
}

呼び出し処理の記述順
class MyClass{
  public MyClass(){
    ~
  }
  public MyClass(int prm){
    System.out.println(“test”);
    this(); //コンパイルエラー
    ※コンストラクタの呼び出しは最初に行わなければならない
  }
  
  public MyClass(int prm){
    this(); // OK
    System.out.println(“test”);
  }
}

PHPではJava等と異なり、サブクラスインスタンス化時にスーパークラスのコンストラクタは自動呼出しされない
明示的に呼び出す必要あり

class classP
{
  public function __construct(){
    ~
  }
}


class classC extends classP
{
  public function __construct(){
    parent::__construct();
    ~
  }
}

初期化ブロック

public class MyClass {
  public MyClass()
  {
    System.out.println("A");
  }
  ※コンストラクタ実行前に呼び出される
  {
    System.out.println("B");
  }
}

public class Base {
  public static void main(String[] args) {
    MyClass m = new MyClass();
  }
}
⇒ B A

初期化ブロックの記述パターン
以下、全てOK。 記述位置に関わらず全てコンストラクタの実行前に実行される。
public class MyClass {
  {
    // コンストラクタの前
  }
  public MyClass(){}
}

public class MyClass {
  public MyClass(){}

  {
    // コンストラクタの後
  }
}
public class MyClass {
  {
    // コンストラクタ無し
  }
}

引数無しコンストラクタを明示的に定義しないクラスを継承した場合のコンストラクタ呼び出し

abstract class Super{
  ※引数無しコンストラクタは定義されていない
  Super(int prm){}
}
class Sub extends Super{
  ※引数有りコンストラクタを呼び出す必要がある。
  Sub(int prm){
    super(prm);
    これはスーパークラスのコントストラクタを呼び出すという意味。
    「Super」クラスのクラス名ではない

  }
  ※↓ はNG。
  Sub(){
    super();
  }

  ※↓ も当然NG。スーパークラスのコンストラクタを呼び出す必要がある。
  Sub(){}

}

継承時のコンストラクタ

public class Parent
{
  public string name { set; get; }
  public int age { set; get; }
  public Parent(string prmName, int prmAge)
  {
    this.name = prmName;
    this.age = prmAge;
  }
}

public class Child : Parent
{
  public string parentname { set; get; }
  public Child(string prmName, int prmAge) : base(prmName, prmAge)
  {
    this.parentname = base.name;
  }
}

public void myForm_Load(object sender, EventArgs e)
{
  Child child = new Child(prmName: "parent1", prmAge: 5);
  
  ⇒child.name = "parent1"
  ⇒child.age = 5
  ⇒child.parentname = "parent1"
}

class MyParent
{
private:
 int myInt1;
 int myInt2;
public:
 MyParent(int prm1, int prm2);
 int GetMyInt1();
 int GetMyInt2();
};

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

int MyParent::GetMyInt1()
{
 return this->myInt1;
}

int MyParent::GetMyInt2()
{
 return this->myInt2;
}

class MyChild : public MyParent
{
private:
 int myInt1;
 int myInt2;
public:
 MyChild(int prm1, int prm2, int prm3, int prm4);
};

※親クラスのコンストラスタに渡す引数を子クラスの引数で引き取る
MyChild::MyChild(int prm1, int prm2, int prm3, int prm4) : MyParent(prm3, prm4)
{
 this->myInt1 = prm1;
 this->myInt2 = prm2;
 return;
}

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

 MyParent *myParent = new MyParent(1, 2);
 cout << myParent->GetMyInt1() << '\n';
 →1
 cout << myParent->GetMyInt2() << '\n';
 →2
 
 MyChild *myChild = new MyChild(1, 2, 3, 4);
 cout << myChild->GetMyInt1() << '\n';
 →3
 cout << myChild->GetMyInt2() << '\n';
 →4
 
 return 0;
}

Public Class Parent
  Private _name As String
  Private _age As Integer
  
  Public Sub New(ByVal prmName As String, ByVal prmAge As Integer)
    Me._name = prmName
    Me._age = prmAge
  End Sub
  
  Public Property name As String
    略
  End Property
  Public Property age As Integer
    略
  End Property
End Class

Public Class Child
  Inherits Parent
  
  Private parentname As String
  
  Public Sub New(ByVal prmName As String, ByVal prmAge As Integer)
    MyBase.New(prmName:=prmName, prmAge:=prmAge)
    
    Me.parentname = MyBase.name
    
  End Sub
  
End Class

Public Class myForm

  Private Sub myForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  
    Dim child As New Child(prmName:="parent1", prmAge:=5)
    
    ⇒child.name = "parent1"
    ⇒child.age = 5
    ⇒child.parentname = "parent1"
    
  End Sub

End Class

class Parent{
  int member;
  
  //コンストラクタ
  Parent(){
    this.member = 0;
  }
  
  //コンストラクタのオーバーロード
  Parent(int prm){
    this.member = prm;
  }
}

class Child extends Parent {
  
  //コンストラクタ
  基底クラスの引数無しコンストラクタが自動的に呼ばれる
  Child(){}
  
  //コンストラクタのオーバーロード
  Child(int prm){
    //明示的に別のコンストラクタを呼び出す場合
    super(prm);
  }
}

PHPではJava等と異なり、サブクラスインスタンス化時にスーパークラスのコンストラクタは自動呼出しされない
明示的に呼び出す必要あり

コピーコンストラクタ

関数へインスタンスを渡す際に実行されるコンストラクタ
通常は、コンストラクタは実行されない。

class MyClass
{
protected:
 int myInt;
public:
 MyClass(int prm = 0){
  this->myInt = prm;
 };

 // コピーコンストラクタ
 MyClass(const MyClass &m)
 {
  this->myInt = m.myInt + 1;
  m.myInt:5
  this->myInt:149405060
  ↓
  this->myInt:6

 };

 int fnGet(){
  return this->myInt;
 };
};

int fnPlus(MyClass prmM);

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

 MyClass m(5);
 int ret;
 
 ret = m.fnGet();
 ret:5

 ret = fnPlus(m);
 ret:6

 return 0;
}

このタイミングでコピーコンストラクタが実行される
int fnPlus(MyClass m)
{ 
 return m.fnGet();
}

コピーコンストラクタ実行のタイミング
MyClass m1(5);
int ret;

【初期化時(コピー)】
MyClass m2 = m1;
コピーコンストラクタが実行される

ret = m2.fnGet();
ret:6
ret = fnPlus(m2);
ret:7

【代入時(非コピー)】
m2 = m1;
コピーコンストラクタが実行されない

ret = m2.fnGet();
ret:5
ret = fnPlus(m2);
ret:6

コンストラクタのスコープ

アクセス修飾子としてpublic、protected、privateが利用可能
スコープはアクセス修飾子に従う。
class PubClass{
  public PubClass(){}
}
class ProClass{
  protected ProClass(){}
}
class PriClass{
  private PriClass(){}
}
public static void main(String[] args) {
  PubClass pub = new PubClass(); ⇒OK
  ProClass pro = new ProClass(); ⇒OK
  PriClass pri = new PriClass(); ⇒コンパイルエラー
}

静的ジェネリッククラスコンストラクタ

プログラム言語 ジェネリック/静的ジェネリッククラスコンストラクタ」参照

デストラクタ

インスタンス破棄時に呼び出されるメソッド
多くの言語では明示的に呼び出すことはできない。

private class clsTest
{     
  //コンストラクタ(クラス名と同じ)
  public clsTest()
  { Console.WriteLine(2); }

  public void method()
  { Console.WriteLine(3); }

  //デストラクタ(「~」+クラス名)
  ~clsTest()
  { Console.WriteLine(5); }
}

static void Main(string[] args)
{
  Console.WriteLine(1);
  myFunc();
  Console.WriteLine(4);
  
  1,2,3,4,5か、1,2,3,5,4の順に出力される。
  4が先か5が先か?はメモリの解放を司るOSの管理。
}

static private void myFunc()
{
  clsTest test = new clsTest();
  test.method();
}

class MyClass
{
public:
  MyClass();
  ~MyClass();
};

MyClass::MyClass()
{
  ~
};


デストラクタ
MyClass::~MyClass()
{
  インスタンスのdelete処理後に処理される
  ~;
}

int main(array<System::String ^> ^args)
{
  MyClass *myClass = new MyClass();
  myClass->~;
  delete myClass;
  
  return 0;
}

特にデストラクタが必要な場面
class MyClass{
private:
 インスタンス変数をポインタで定義
 int *ary;
public:
 MyClass(int prm[]);
 ~MyClass();
 int Get(int elmCnt);
};

MyClass::MyClass(int elm[])
{
 ポインタにnewで作成した配列を格納
 newで確保されたメモリ領域は明示的にdeleteしないと消えない
 ary = new int[] { elm[0], elm[1], elm[2] };
}

MyClass::~MyClass()
{
 delete ary;
}

int MyClass::Get(int elmCnt)
{
 return this->ary[elmCnt];
}

int _tmain(int argc, _TCHAR* argv[])
{ 
 int array[] = { 1, 2, 3 };
 MyClass *m = new MyClass(array);
 int ret = m->Get(2);
 ret:3
 delete m
 
 return 0;
}

class Foo{
  //デストラクタ
  finalize(){
    ~
  }
}
class classP
{
  public function __destruct(){
    ~
  }
}


class classC extends classP
{
  public function __destruct(){
    スーパークラスのデストラクタを明示的に呼び出す
    parent::__destruct();
    ~
  }
}

コピーコンストラクタ終了時の処理

class MyClass
{
public:
 デフォルトコンストラクタ
 MyClass()
 {
  cout << "デフォルト" << '\n';
 }
 コピーコンストラクタ
 MyClass(const MyClass &prmClass)
 {
  cout << "コピー" << '\n';
 }
 デストラクタ
 ~MyClass()
 {
  cout << "デストラクタ" << '\n';
 }
};

void myFunc(MyClass m)
{
 cout << "サブルーチン" << '\n';
}

void myCall()
{
 MyClass m;
 myFunc(m);
}

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

 myCall();
 return 0;
}

結果
デフォルト

コピー
サブルーチン開始時(インスタンスコピー時)にコピーコンストラクタが実行される

サブルーチン

デストラクタ
サブルーチン終了時(コピーインスタンス破棄時)にデストラクタが実行される

デストラクタ
インスタンス破棄時にもデストラクタが実行される

明示的な開放処理

デストラクタ(の代わりの処理)の実行

class MyClass : IDisposable
{
 public MyClass()
 {
  Console.Write(@"<p>");
 }
 public void Dispose()
 {
  Console.Write(@"</p>");
 }
 Disposeの実装が強制される
}

static void Main(string[] args)
{

 using (var m = new MyClass())
 {
  Console.Write(@”Hello”);
 }
}
結果:<p>Hello</p>

イニシャライザ

JAVA限定。
クラスファイルがロードされたタイミングで実行されるメソッド。
コンストラクタよりも早い。

class Foo{
  //イニシャライザ
  static{
    ~
  }
  //コンストラクタ
  Foo{
    ~
  }
}