next up previous
Next: 6 練習 Up: 計算天文学 II 第5回 常微分方程式の初期値問題(1) Previous: 4 線形多段階法

5 構造体とクラス

今回はちょっと C++ 言語の話題というか、今回以降の話で便利な機能につい て。

偏微分方程式とか、あるいは連立常微分方程式の数値解法をプログラムするの に、普通は配列を使う。しかし、これは割合に面倒だし、いろいろ妙な間違い をする可能性も高い。例えば、 $\mbox{\boldmath$x$}$ $\Delta \mbox{\boldmath$x$}$ を足すのは、数式と しては $\mbox{\boldmath$x$}+ \Delta \mbox{\boldmath$x$}$ で済むのに、例えば C では

for(i=0;i<n;i++) x[i] += dx[i];
Fortran なら
      do i = 1, n
          x(i) = x(i) + dx(i)
      enddo
という具合で、数式なら4文字で済むところをその何倍も書かないといけない。 これは、 C にしても Fortran にしても、配列(あるいはベクトルとか行列) といった、数学では基本的な要素であるものに対する演算を直接に表現する記 法を持っていないからである。

まあ、Fortran 95 とかそういったものを使うとそういう記法があるという話 もないわけではないが、あまり普及していない。普及していない理由はいろい ろあるが、その一つは C++ を使えば同様なことが実現できるからである。

C++ 言語自体は、配列に対する演算というものを用意しているわけではない。 しかし、C++ では、プログラムの中で新しい「型」(実数型とか整数型という のと同じ意味での)を定義して、さらにそれに対する演算を定義することがで きる。これでベクトル型とかいったものを自分の使いやすいように定義すれば いいことになる。

例えば、非常に基本的なベクトル型の定義は以下のようなものになる。ここで は加算と入出力くらいしか定義していないが、他の必要な演算も同様に定義で きる。一応使いそうな演算を定義したものが

http://grape.astron.s.u-tokyo.ac.jp/~makino/pcphysics/programs/vector.h
にあるので、実際にプログラムを作る時にはこれを使ってもよい。牧野が書い たプログラムは信用できないという向きは自分で書くこと。

/------------------------------------------------------
/  vector  --  a class for VLEN-dimensional vectors
/  VLEN  must be defined as constant before this header
/------------------------------------------------------
#ifndef  _VECTOR_H
#  define  _VECTOR_H
class vector
{
private:
    double element[VLEN];
public:
    vector(double c=0 ) {for (int i=0;i<VLEN;i++)element[i]=c;}
    double & operator [] (int i)       {return element[i];}
    friend const vector operator + (const vector &,
                                    const vector &);
    vector& operator += (const vector& b)
    {for(int i=0; i<VLEN;i++)element[i] += b.element[i];       
    return *this;}
    friend ostream & operator <<(ostream & , const vector & );
    friend istream & operator >> (istream & , vector & );
};

inline ostream & operator <<(ostream & s, const vector & v)
{
    for(int i=0; i<VLEN;i++)s <<v.element[i] <<"  " ;
    return s;
}
inline istream & operator >> (istream & s, vector & v)
{
    for(int i=0; i<VLEN;i++)s >> v.element[i];
    return s;
}
inline const vector operator + (const vector &v1,
                                const vector & v2)
{
    vector v3;
    for(int i=0; i<VLEN;i++)
        v3.element[i] = v1.element[i]+ v2.element[i];
    return v3;
}
typedef const vector (vfunc)(const vector &); 
#endif

実際にこれを使うには、

const int VLEN = 2;
#include  "vector.h"

double k;
vector dxdt(vector & x)
{
    vector d;
    d[0] = x[1];
    d[1] = -k*x[0];
    return d;
}

    .....
    double h;
    vector kx1;
    kx1 = dxdt(x)*h;
    ......

というような具合に、ベクトルの大きさを指定する(これはここでは固定であ る)。もちろん、可変にするとかいろんなことができるが、繁雑になるのでこ こでは固定サイズにする。

ここで、注意してほしいのは、 + という演算子や [] という、 これも「演算子」が、新しく定義されていることである。これらは、もちろん + なら実数や整数に対してすでに定義されているが、ここではベクトル 同士の演算に対して新しい意味を持つように拡張されていることになる。 [] も同様で、配列の他にここで定義したベクトル型について新しい意味 を持つようになったわけである(といっても、こちらは普通の配列に対してと まったく同じように働くが)。

これは、偉そうにいうと C++ の演算子多重定義 (overload)という強力な機能 である。まあ、落ち着いて考えてみると、この機能の実現はそんなに難しいわ けではなくて、例えば + という演算子が出てきたところで、それが適 用されているデータ型を見て、そのデータ型に対して定義されている演算を呼 ぶようにするというだけのことである。これは、コンパイラにそういう機能を 付け加えるだけで実現できる。

C++ の場合には、このような、ある意味での機能拡張は、「クラス」というも のを使って実現されている。これはもともとは「オブジェクト指向」とかそう いった難しい機能を実現するためのものであるが、ここではとりあえずあんま りそういうことは考えないでベクトル型を使うことにする。

あ、皆さんが使っているコンパイラが十分新しいと、上のプログラムがそのま までは動かないかもしれない。 これは C++ の新しい標準では vector と いう 語に別の意味を与えているからである。こういうときはしょうがないの で vector を myvector とかそういう別の語に置き換えて使って欲しい。



Jun Makino
平成14年12月2日