++C++; // 未確認飛行 C

Google
Web ufcpp.net

コンストラクタとデストラクタ

目次

キーワード

概要

オブジェクトを作成するためには、オブジェクトを正しく初期化してやる必要があります。 そのために、オブジェクトの構築のためのコンストラクタと呼ばれる特殊なメソッドが用意されています。 また、同様にオブジェクトの破棄のためのデストラクタと呼ばれるものもあります。

コンストラクタ

コンストラクタはインスタンスを正しく初期化するための特別なメソッドです。 コンストラクタは以下のように、クラス名と同じ名前のメソッドを書くことで定義できます。

class SampleClass
{
  // ↓これがコンストラクタ
  SampleClass()
  {
    // インスタンスの初期化用のコードを書く
  }
}

他のメソッドと異なり、戻り値の型は書きません(コンストラクタは戻り値を返すことは出来ません)。

例えば、名簿作成のために個人情報を表す Person というクラスを作ったとします。 説明を簡単にするために、この名簿では名前と年齢だけを管理することにします。 そのため、Personnameage という2つのメンバーのみを定義します。

class Person
{
  public string name; // 名前
  public int age;     // 年齢
}

ここで、Personクラスのインスタンスを生成する際、 名前を "" (空の文字列)で、年齢を 0 で初期化したいとします。 そのためには以下のようなコンストラクタを作成します。

class Person
{
  public string name; // 名前
  public int age;     // 年齢

  // ↓これが Person クラスのコンストラクタ
  public Person()
  {
    name = "";
    age  = 0;
  }
}

コンストラクタは new を用いてインスタンスを作成する際に呼び出されます。 例えば、下記のようなコードを実行した場合、

using System;

class Test
{
  public Test()
  {
    Console.Write("Test クラスのコンストラクタが呼ばれました\n");
  }
}

class ConstructorSample
{
  public static void Main()
  {
    Console.Write("Main の先頭\n");

    Test t = new Test(); // ここで Test のコンストラクタが呼ばれる

    Console.Write("Main の末尾\n");
  }
}

以下のような出力が得られます。

Main の先頭
Sample クラスのコンストラクタが呼ばれました
Main の末尾

また、コンストラクタには引数を与えることもできます。 例えば、先ほどの Person クラスで、 インスタンスの作成時に名前と年齢の値を設定したい場合、 以下のようなコンストラクタを作成します。

class Person
{
  public string name; // 名前
  public int age;     // 年齢

  // ↓引数つきの Person クラスのコンストラクタ
  public Person(string name, int age)
  {
    this.name = name;
    this.age  = age;
  }
}

この例で使われている this というキーワードは、 作成するインスタンス自身を格納する特別な変数です。 そのため、この例では this.namePerson クラス内で定義された name のことになります。 一方、this の付いていない方の name は、コンストラクタの引数として定義した name のことです。

引数つきのコンストラクタを呼び出すためには、new を使ってインスタンスを生成する際に、以下のようにして引数を渡します。

クラス名 変数名 = new クラス名(引数リスト);

例えば、先ほど定義したPersonクラスのコンストラクタを呼び出すためには以下のようにします。

Person p = new Person("ビスケット・クルーガー", 57);
Console.Write(p.age); // 57 と表示される

また、コンストラクタはオーバーロードすることができます。 例えば、Person クラスに、名前と年齢を引数として与えるのコンストラクタと、何も引数を与えないコンストラクタの両方を定義することができます。

class Person
{
  public string name; // 名前
  public int age;     // 年齢

  // ↓引数なしの Person クラスのコンストラクタ
  public Person()
  {
    this.name = "";
    this.age  = 0;
  }

  // ↓引数つきの Person クラスのコンストラクタ
  public Person(string name, int age)
  {
    this.name = name;
    this.age  = age;
  }
}

サンプル

using System;

/// <summary>
/// 名簿用の個人情報記録用のクラス。
/// とりあえず、名前と年齢のみ。
/// </summary>
class Person
{
  // public なメンバー変数
  public string name; // 氏名
  public int    age;  // 年齢

  // 定数
  const int UNKNOWN = -1;
  const string DEFAULT_NAME = "デフォルトの名無しさん";

  /// <summary>
  /// 名前と年齢を初期化
  /// 与えられた年齢が負のときは年齢不詳とみなす
  /// </summary>
  /// <param name="name">氏名</param>
  /// <param name="age">年齢</param>
  public Person(string name, int age)
  {
    this.name = name;
    this.age  = age > 0 ? age : UNKNOWN;
  }

  /// <summary>
  /// 名前のみを初期化
  /// 年齢は不詳とする
  /// </summary>
  /// <param name="name">氏名</param>
  public Person(string name) : this(name, UNKNOWN)
  {
  }

  /// <summary>
  /// デフォルトコンストラクタ
  /// 氏名・年齢ともに不詳
  /// </summary>
  public Person() : this(null, UNKNOWN)
  {
  }

  /// <summary>
  /// 文字列化
  /// 氏名が不詳のときには NONAME に設定された名前を返す
  /// 年齢が不詳の時には名前のみを返す
  /// 氏名・年齢が分かっているときには「名前(xx歳)」という形の文字列を返す
  /// </summary>
  public override string ToString()
  {
    if(name == null)
      return DEFAULT_NAME;

    if(age == UNKNOWN)
      return name;

    return name + "(" + age + "歳)";
  }
}//class Person

//----------------------------------------------------
// メインプログラム
class ConstructorSample
{
  public static void Main()
  {
    Person p1 = new Person("ちゆ", 12);
    Person p2 = new Person("澪");
    Person p3 = new Person();

    Console.Write("{0}\n{1}\n{2}\n", p1, p2, p3);
  }
}
ちゆ(12歳)
澪
デフォルトの名無しさん

デストラクタ

コンストラクタとは逆に、インスタンスが破棄されるときに呼び出されるのがデストラクタです。 デストラクタは以下のように、クラス名の前に ~ を付けた名前のメソッドを書くことで定義できます。

class SampleClass
{
  // ↓これがデストラクタ
  ~SampleClass()
  {
    // インスタンスの破棄用のコードを書く
  }
}

デストラクタはコンストラクタと違って、引数を持つことができません。

.NET Framework では、インスタンスの寿命は .NET Framework 自体が管理していて、 いつインスタンスの破棄が行われるのかは分かりません。 また、プログラムの終了時にはデストラクタが呼び出されない場合もあります。

using System;

class Test
{
  public Test()
  {
    Console.Write("Test クラスのコンストラクタが呼ばれました\n");
  }

  ~Test()
  {
    Console.Write("Test クラスのデストラクタが呼ばれました\n");
  }
}

class DestructorSample
{
  public static void Main()
  {
    Console.Write("1\n");
    Test t = new Test(); // ここで Test のコンストラクタが呼ばれる
    Console.Write("2\n");
    t = null;            // ↑で作成したインスタンスはもう利用されなくなる
                         // でも、デストラクタはまだ呼ばれない
    Console.Write("3\n");
  }
}
1
Test クラスのコンストラクタが呼ばれました
2
3
Test クラスのデストラクタが呼ばれました

この例では、デストラクタはプログラムの終了時にちゃんと呼び出されていますが、 呼び出されない場合もあります。 このような性質を持っているため、通常、デストラクタはあまり利用されません。 何らかのリソース(ファイルやプリンタなど)の破棄(ファイルのバッファのフラッシュやプリンタの解放)を行う必要がある場合、Dispose というメソッドと、 using 文を用います。 using 文については後ほど説明します。

演習問題

問題 1

前節クラス問題 1Point 構造体および Triangle クラスに、 以下のようなコンストラクタを追加せよ。

/// <summary>
/// 座標値 (x, y) を与えて初期化。
/// </summary>
/// <param name="x">x 座標値</param>
/// <param name="y">y 座標値</param>
public Point(double x, double y)
/// <summary>
/// 3つの頂点の座標を与えて初期化。
/// </summary>
/// <param name="a">頂点A</param>
/// <param name="b">頂点B</param>
/// <param name="c">頂点C</param>
public Triangle(Point a, Point b, Point c)

解答

Transtation into English

[お問い合わせ](q)