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

Google
Web ufcpp.net

定数

目次

キーワード

概要

全く変化しない値を、異なる場所で何度も使いたい事があります。 このような場合、リテラルを何箇所にも分散させて書くのではなく、const というキーワードを用いて定義した定数を使うべきです。

変化しない値

例えば、以下のようなコードを見てください。

int[] array = new int[5];

for(int i=0; i<5; ++i)
  array[i] = int.Parse(Console.ReadLine());

int sum = 0;
int sq_sum = 0;

for(int i=0; i<5; ++i)
{
  int n = array[i];
  sum += n;
  sq_sum += n*n;
}

double mean = sum / 5;
double var  = sq_sum / 5 - mean*mean;

Console.Write("平均: {0}\n分散: {1}\n", mean, var);

値を5つ入力してもらって、その平均と分散を求めるものです。 5 というリテラルが4箇所出てきていますね。

さて、ではここで、データの数を5つではなくて6つに変更することになったとします。 5 というリテラルを全部 6 に置き換える必要があるわけですが、 たった4つでも結構面倒です。 まして、もっと数が多かったことを考えてみましょう。 数が増えるにつれて、忘れず全部修正するのが困難になります。

なので、普通はリテラルを直接使うということはしません。 例えば、以下のように、5 と言う値を一度変数に代入して使うことを考えます。

int NUM = 5;
int[] array = new int[NUM];

for(int i=0; i<NUM; ++i)
  array[i] = int.Parse(Console.ReadLine());

int sum = 0;
int sq_sum = 0;

for(int i=0; i<NUM; ++i)
{
  int n = array[i];
  sum += n;
  sq_sum += n*n;
}

double mean = sum / (double)NUM;
double var  = sq_sum / (double)NUM - mean*mean;

Console.Write("平均: {0}\n分散: {1}\n", mean, var);

これで、もしデータの個数を変更する必要が生じても、 int NUM = 5; の1行だけの修正で解決します。

const

ところが、このコードにもちょっとだけ問題があります。 1つは、定数なのか、途中で値が変わるものなのかが分からないことです。 変数なので、途中で値が書き換えられてしまってもエラーにはなりません。 また、ソースファイルの見易さの観点からも、 定数は定数であることが一目で分かる方が好ましいです。

2つ目の問題は、効率面にあります。 書き換える必要のある変数よりも、 その必要のない定数の方が、プログラムの実行効率が高くなります。 したがって、上述のような方法(一度変数に値を格納)すると、 多少ですが実行効率が悪くなるという欠点があります。

そこで、C# では、const というキーワードを用いることで、 変数のように扱える定数を定義することが出来ます。 通常、定数(constant)とだけいうと、 リテラルではなく、 こちらのことを指します。 (リテラルの方は直定数と訳す。)

const int NUM = 5;
int[] array = new int[NUM];

for(int i=0; i<NUM; ++i)
  array[i] = int.Parse(Console.ReadLine());

int sum = 0;
int sq_sum = 0;

for(int i=0; i<NUM; ++i)
{
  int n = array[i];
  sum += n;
  sq_sum += n*n;
}

double mean = sum / (double)NUM;
double var  = sq_sum / (double)NUM - mean*mean;

Console.Write("平均: {0}\n分散: {1}\n", mean, var);

const を付けて宣言された定数は、宣言文中における初期化時にのみ値を代入できます。 定数というくらいですから、当然、 その他の場所で値を書き換えることは出来ません。

const int NUM = 5; // 宣言時の初期化のみ可能
NUM = 6; // ここでエラーになる

また、const を付けた定数を用いたソースコードは、 リテラルを使ったソースコードと同等のコンパイル結果になります。 従って、リテラルの直書きと比べて効率が落ちることはありません。

ただし、const キーワードは、int などの数値型、string 型、または列挙型に対してのみ使用できます。 (あと、値が null 限定で参照型にも使える。) インスタンスを new キーワードで生成するようなものには const キーワードは使えません。

const メンバ

const を使った定数は、 メソッド中(ローカル)だけでなく、 クラスのメンバにする事も出来ます。

class Math
{
  public const double PI = 3.1415926535897932;
}

const メンバはクラスに属します。 (静的変数と同じ扱い。 ClassName.Member という形式で参照。) 例えば、上述の例、PI の場合、Math.PI という形式で参照します。

ちなみに、private な場合は const メンバ変数で問題ないのですが、 public にする場合にはあまり const メンバ変数は使わない方がいいです。 数学の定数である π のように、 まあまず仕様変更で値が変わるとかありえないような定数なら全然問題もないのですが、 もしも変更がありうる場合には、 たとえ定数であっても static なプロパティにしておく方がいいです。 でないと、値が変わったときに、利用側でも再コンパイルが必要になってしまいます。

readonly

クラスのメンバに対しては、 const 以外に、もう1つ定数のようなものを実現する方法があります。 readonly というキーワードを用いて、読取り専用(read only)の変数を定義できます。 const との違いは以下のようになります。

constreadonly
ローカル変数にも使えるクラスのメンバ変数のみ。
常に静的変数と同じ扱い。static の有無を変えられる。
宣言時にのみ初期化可能。コンストラクタ内で値を書き換え可能。
コンパイル結果はリテラルと同等。コンパイル結果は変数と同等。
インスタンスを new で生成するようなものには使えない。new 可能。
class A
{
  readonly int num;

  public A(int num)
  {
    this.num = num; // コンストラクタ内では書き換え可能。
  }

  public void Method(int num)
  {
    int x = this.num; // 読み取りは可能。
    this.num = num;   // 書き込み不可。エラー!
  }
}

ちなみに、const を使った定数は、 (コンパイル結果がリテラルを使った結果と同じく) プログラム中に直接値が埋め込まれてしまうため、 値を変更した際には、参照側(クラス利用側)のコードも再コンパイルする必要が生じます。 なので、Math.PI(数学定数π)のように、本当に不変で、 絶対に変わることのない値以外は public const なメンバ変数にすべきではありません。 (private なものや、ローカル変数に対する const は OK。)

Transtation into English

[お問い合わせ](q)