D言語入門記事 ~クラス(3)~

アバター
tk-xleader
記事: 158
登録日時: 14年前
連絡を取る:

D言語入門記事 ~クラス(3)~

投稿記事 by tk-xleader » 13年前

今日はクラスの特殊な意味を持つメンバ関数について書きます。

[1] コンストラクタ

 クラスオブジェクトの変数を、定数ではなくてパラメーターによって初期化したいときは、コンストラクタというメンバ関数を定義します。コンストラクタは、戻り値型なしで、this という名前で関数を定義します。

CODE:

class Car{
        private int fuel = 0; //燃料の量
        public this(int fuel){ //これがコンストラクタ
        	this.fuel = fuel;
        }
        public void supplyFuel(int amount){
                fuel += amount;
        }
        public void drive(int distance,int speed){
                fuel -= distance * speed / 2;
        }
        public int getFuelAmount(){
                return fuel;
        }
}
 使う側はどうするのかというと、オブジェクトを生成するとき、つまりnew演算子のところで引数を渡すわけですが、こうなります。

CODE:

Car car = new Car(100);
 コンストラクタは、必要に応じて別のコンストラクタに処理を委譲することが出来ます。この場合、内部でthisという関数を呼び出すコードを書くことになりますが、複数のコンストラクタが互いに呼び出している場合、コンパイルエラーになります。

CODE:

class Airplane{
	int fuel;
	Person[] passengers;
	
	public this(int fuel,Person[] passengers){
		this.fuel = fuel;
		this.passengers = passengers;
	}
	public this(int fuel){
		this(fuel,null) //処理を、上のコンストラクタに丸投げしている。
	}
	
	/*…以下略…*/
}
[2] デストラクタ

 コンストラクタはオブジェクトの生成時に呼ばれるメンバ関数です。逆に、オブジェクトが破棄されるときに呼ばれる関数が、デストラクタです。デストラクタは、~this という名前の、引数と戻り値が共にない関数です。つまり…

CODE:

class Sample{
	// デストラクタ
	~this(){
		/*…実装略…*/
	}
	
	/*…以下略…*/
}
 D言語では、基本的にはオブジェクトはGCが必要に応じて破棄します。ですから、デストラクタは、決まったタイミングで呼び出されるという類のものではありません。もし決まったタイミングでデストラクタを呼び出したいということであれば、scope属性を使うことになります。scopeについては別の記事で解説します。

[3] プロパティ

 クラスのメンバ変数は、必ずprivateであるべきです。これは、意図しないアクセスによってオブジェクトの整合性が失われることを防止する為ですが、外部からのアクセスを、クラス側でチェックできるならば、正当なアクセスを許可することは、全く問題がないでしょう。そこで、オブジェクト指向プログラミングでは、クラスにアクセッサという関数を用意することがあります。
 アクセッサは、大抵は"get~" "set~" といった名前を付けられます。当然、前者が値の読み取り、後者が値の書き込みに対応しているわけです。D言語でも、アクセッサを使う方法は全く間違いではありませんが、このような用途のために用意されている言語機能が、プロパティです。

 プロパティとは、あたかもメンバ変数にアクセスするかのようにアクセスできる、メンバ関数のことです。つまり、こういうことが出来るわけです。D言語でプロパティを定義するには、@property属性をつけて、get~に対応するほうは引数なしで、set~に対応するほうは引数一つで関数を定義するだけです。呼び出しは、戦術の通りメンバ変数にアクセスするのと同じやり方でアクセスします。以下サンプル。

CODE:

import std.stdio;

class Sample{
	private int value = 100;
	
	public @property{
		int Value(){ //"get~"に相当する
			writeln("Called int Value()!!");
			return value;
		}
		int Value(int newValue){ //"set~"に相当する
			writeln("Called int Value(int newValue)!!");
			return this.value = newValue;
		}
	}
}

void main(){
	Sample sample = new Sample;
	
	writefln("%d",sample.Value); //読み取り
	
	sample.Value = 1000; //書き取り
	
	writefln("%d",sample.Value); //読み取り
}
実行結果
Called int Value()!!
100
Called int Value(int newValue)!!
Called int Value()!!
1000


前の記事「属性(1)とクラス(2)」 ←→ 次の記事「コンテキスト参照とthis (2)
最後に編集したユーザー tk-xleader on 2012年6月18日(月) 01:17 [ 編集 2 回目 ]

naohiro19
記事: 256
登録日時: 15年前

Re: D言語入門記事 ~クラス(3)~

投稿記事 by naohiro19 » 13年前

delete しなくて大丈夫なんですか?
勘違いしていたので取り消し線をしました。
最後に編集したユーザー naohiro19 on 2012年6月11日(月) 20:27 [ 編集 1 回目 ]

アバター
tk-xleader
記事: 158
登録日時: 14年前
連絡を取る:

Re: D言語入門記事 ~クラス(3)~

投稿記事 by tk-xleader » 13年前

D言語では、deleteしなくても、GCが、必要に応じてオブジェクトの破棄とヒープ領域の解放を実行しますから、問題は生じないです。
一方で、手動でdeleteすることも出来ます。この場合、オブジェクトの破棄は確実に実行されます(つまり、デストラクタが呼ばれるということ。)し、そのオブジェクトが占有していたヒープ領域は解放されます。
その上で、delete演算子のオペランドに指定されたポインタ/動的配列/参照型の変数は、nullが代入されます。これによって、少なくともその変数によって解放済みの領域にアクセスすることは防止されています。