[1] 属性を付与する
属性というのは、関数や変数などにつけることによって、それらの性質を定めるものです。具体的には、宣言の可視範囲や、関数の呼び出し規約などを定めるものです。複数の属性を付けることも出来ますが、それらが互いに矛盾する場合は出来ません。
*主な属性リスト
・private,protected,public,export
これらは、アクセスできる範囲を定めるものです。右から順に、可視範囲が広がっていきます。
private ……… 宣言しているスコープ内部からしかアクセスできない。
protected ……… 所属するクラスとその派生クラスからしかアクセスできない。
public ……… プログラム内部からなら何処からでもアクセスできる。
export ……… プログラム外部も含めて何処からでもアクセスできる。
これに加えて、package属性というものが存在します。これは、同じパッケージに属するモジュールからのアクセスは許可するものです。モジュールシステムとパッケージに関しては、日を改めて解説します。
・auto,static,__gshared,scope,synchronized
これらには、まず、変数に付与して、その変数の性質を定めるものとしての意味があります。これに関しては、「定数の表現(2)と変数」の項で既に書いています。
scope属性は、クラスに付与することも出来ます。この場合、クラスオブジェクトをscope変数以外で参照することが出来なくなります。
synchronizedは、クラスや関数に付与することが出来ます。synchronized属性が付けれられている場合、マルチスレッド環境においては、同期されることになります。
・const,immutable
これも変数やクラスに、属性として付与することが出来ます。これらをクラスにつけた場合、クラスの全てのメンバにその属性が付与されることになります。
・@disable属性
この属性を付加されている宣言は、アクセスした瞬間にコンパイルエラーになります。この属性は、整数型に対して関数がオーバーロードされているときに、暗黙変換をするのではなくコンパイルエラーにしたいときなどに使います。要するに、アクセスして欲しくない宣言に対して付与する属性です。
void func(real value){
/*実装略*/
}
@disable void func(int dummy);
void main(){
func(0.0); //OK
//func(0) コンパイルエラー、func(int)は@disable属性が付けられているので、アクセス不可。
}
これは、互換性のためだけに残されている古いAPI関数など、使うことが推奨されない関数やクラスなどに指定するものです。コンパイラにもよりますが、コンパイルオプションによって、警告や、あるいはエラーを出すようにチューニングすることが出来るでしょう。
・リンケージ属性とextern属性
リンケージ属性とは、リンク形式などを他の言語と刷り合わせる為の属性です。書式は次の通りです。
extern( リンケージ指定 )
ex) extern(C) char* strchr(in char* s, int c);
リンケージ宣言のところでは、「D」,「C」の2つは必ずサポートされ、それぞれD言語とC言語の名前マングリング規則や呼び出し規約に従うことになっています。そのほか、Windows環境では、extern(Windows)もサポートされます。これはStdCall呼び出し規約に対応するものですから、Windows APIや、自作のDLLからエクスポートされている関数を呼び出すとき、そして、Windows APIが必要とするコールバック関数を定義するときなどに使うことになります。
extern属性は、そのモジュールでは実体を定義しない変数の宣言に使います。よく使われるのが、C言語で書かれたライブラリの中で定義されているグローバル変数への宣言を記述するときに使うことが出来ます。
・アラインメント属性
これは、構造体メンバのアラインメントを指定するのに使います。構造体についてはまた後日解説します。
*属性の付加方法
宣言に属性を付加する方法は、次の3通りです。
・宣言に直接付加する。
これは、最も簡単な属性の付与の方法です。宣言の前に属性を並べるだけです。
private int value;
//Windows APIのMessageBoxW関数の宣言
export extern(Windows) int MessageBoxW(HWND hWnd,LPCWSTR lpszText,LPCWSTR lpszTitle,UINT uType);
scope class FileLock{
/* 実装略 */
}
そのまんまですが、新しいスコープを導入して、その内部の宣言の全てに指定した属性を付与するという方法です。
//FILE_MAX定数,remove_file関数,Socketクラスのアクセス属性は全てpackageになる。
package{
immutable int FILE_MAX = 4996;
void remove_file(const(char)[] fileName){
/*実装略*/
}
scope class Socket{
/* 実装略 */
}
}
これは、それ以降の宣言に付加される属性を変更するという方法です。
//モジュールレベル宣言のアクセス属性のデフォルトはpublic
int Average(in int[] value_list){
return Sum(value_list)/value_list.length;
}
package: //ここから下のアクセス属性はpackage属性
immutable int FILE_MAX = 4996;
void remove_file(const(char)[] fileName);
scope class Socket{
/* 実装略 */
}
private: //ここから下はprivate属性になる。
int Sum(in int[] value_list){
int result;
foreach(c; value_list){
result += c;
}
return result;
}
クラス宣言の形式は、こうなります。ちなみに、クラス名はD言語の慣例として、UpperCamelCaseといって、キャピタライズ(つづりの最初を大文字にすること)された単語を直接つなげる形式が使われます。とはいっても、これは好みの問題です。コードスタイルについては、趣味の範囲内においては、個々人の自由ではないかと僕は思っています。
メンバ関数の命名については、慣例で、lowerCamelCaseといって、2つ目以降の単語はキャピタライズして、それらを直接つなぐ方式が取られます。メンバ変数については、普通はprivateとして宣言されるでしょうから、特に定められた規則は無いですが、統一しておいたほうが分かりやすいでしょう。要するに、外部からアクセスされる識別子について、ちゃんと規則を持たせておくことが重要なわけです。
つまり、クラス内部で関数や変数宣言をすれば、それらはクラスに属するメンバになるということです。
class Car{
private int fuel = 0; //燃料の量
public void supplyFuel(int amount){
fuel += amount;
}
public void drive(int distance,int speed){
fuel -= distance * speed / 2;
}
public int getFuelAmount(){
return fuel;
}
}
クラスは、参照型として扱われます。つまり、オブジェクトを指し示す参照変数によってオブジェクトを操作するのです。オブジェクトの生成は、new式を使って行うことが出来ます。いろいろな形式がありますが、次の形式が最もシンプルです。
► スポイラーを表示
また、オブジェクトのメンバにアクセスするには、参照.メンバ という形を取ります。特に、メンバ関数の呼び出しは、参照.メンバ関数(引数………)となります。
import std.stdio;
class Car{
private int fuel = 0; //燃料の量
public void supplyFuel(int amount){
fuel += amount;
}
public void drive(int distance,int speed){
fuel -= distance * speed / 2;
}
public int getFuelAmount(){
return fuel;
}
}
void main(){
Car car = new Car;
car.supplyFuel(100);
writefln("%d",car.getFuelAmount());
car.drive(3,20);
writefln("%d",car.getFuelAmount());
car.supplyFuel(20);
writefln("%d",car.getFuelAmount());
}
100
70
90
前の記事「オブジェクト指向とクラス(1)」 ←→ 次の記事「クラス(3)」