C++と Java の設計思想と機能の比較〜筑波 EDP-skillup:システム開発技術を考える〜
久野靖

2007.9.26

はじめに

1

プログラミング言語の体系□ 久野の専門分野…

1.1

ソフトウェア開発とプログラミング言語

0• プログラミング言語 --- オブジェクト指向言語、並 □ ソフトウェア開発のあり方とプログラミング言語は密接
行/並列言語、教育用言語に関係• システムソフトウェア --- 「コンピュータを使うた• COBOL: データ中心、定型的事務処理

めのソフト」→「何をどう作ったら役に立つ?」(言• FORTRAN: 科学技術計算

語処理系、コンパイラもこれに含まれる)• C: ネットワークなどシステム機能を用いる処理• ユーザインタフェース --- コンピュータと人間の接点、対話方式など□ 近年のソフトウェア開発の主流の 1 つ→ Web アプリケー• 情報教育、情報技術教育 --- ICT の何を、どこま
で、どのように教えるか?

ション(中学・高校、大学、社• サーバ側開発言語→ PHP、Perl など

会人…)• クライアント側言語→ JavaScript (連携:Ajax)□ 本日の内容構成□ スクリプト言語の利点→弱い型、少量の記述で多くの機• 1 章 --- プログラミング言語の体系と C++、Java の能、短い開発サイクル

位置づけ• その反面、大規模な開発には記述面、性能面で不利• 2 章 --- C++言語の由来とあらまし□ 大規模ソフトウェア開発に必要な機能• 3 章 --- Java 言語の由来とあらまし• 4 章 --- C++と Java の違うところ←機能←設計思想• 強い型検査→コンパイラによる整合性検査• ----- 以 下 は 時 間 的 余 裕 と 皆 様 の 希 望 に 応 じ て• モジュール→名前の衝突等を防ぐ、部品化を可能にする----• 5 章 --- 実行時の動的処理と注記 (Java が持つ特色)

• オブジェクト指向→オブジェクト指向はなぜ必要?

• 6 章 --- 総称的プログラミング (主に C++、一部

1.2

Java)
• 7 章 --- テンプレートメタプログラミング (C++)

オブジェクト指向言語□ オブジェクト指向とは…

• 8 章 --- テンプレートパラメタの仕様記述機構• プログラムが扱う対象を「もの」(オブジェクト) と(C++、研究レベル)

捉える「考え方」• オブジェクトの種類ごとに「属性」「操作 (動作)」が付随• 人間は「もの」の世界に生きている→「もの」中心が考えやすい1• ヨーロッパ発の言語であり、あまり普及しないまま• 考えやすい (把握しやすい) ことはプログラミング言

忘れられる語にとって重要 (「考えやすい」言語の方が同じ努力であればより複雑なものまで作れる)□ Smalltalk-80 (1980) --- オブジェクト指向の再発見□ 動的分配…オブジェクト指向言語の重要な機能• 例:

道路交通「自動車. 前進 ()」「自転車. 前進 ()」GUI 部品を駆使 →センセーショナルな登場

等の操作があるとする• 「x. 前進 ()」で「x に現在入っているオブジェクト• 当時のプログラミング言語は「モジュール」「抽象デーの前進」が呼ばれる (実行時に定まる) →動的分配のタ型」の段階→オブジェクト指向を取り入れること機能の可能性を提示• 言語自体は Lisp などに近い型の無い言語、実用のソ• OO 以 前:「if(x が 自 動 車) 自 動 車 の 前 進 (x);elsif(x が自転車) 自転車の前進 (x); elsif...」をやっていた (コードが長く読みづらい)• 動的分配のため x が「何であっても」同じ 1 つのコードでよい (総称的)class」として開発• クラス方式の他にプロトタイプ方式があるがクラス• 効率が良い (C 言語に劣らない性能が目標) →支持を方式が主流

集める→ C 言語に基づいたオブジェクト指向言語として支配的な立場• クラス間の「継承」により短い記述で類似したクラスが作れる (差分プログラミング) ←現在ではあまり□ Java

推奨されない• C++(高速だが危険なこともできる) のアンチテーゼ• クラスを「型」として扱うことで強い型検査を適用として Sun Microsystems 社で開発してコードの誤り「の一部」をコンパイル時検査できる• 安全、互換性、標準ライブラリの充実などで支持を集める□ クラスで記述するもの (細かいものは略)□ その他… Eiffel など (ヨーロッパ系はどうしてもマ• 各オブジェクトが持つデータ (=インスタンス変数)

イナー)• 各オブジェクトが持つ操作 (=メソッド)□ C++/Java のコンパイル文化とは別に、スクリプト言語

Point (2 次元平面上の点) の場合…

• データ:• 操 作:• この後、「実用言語にオブジェクト指向導入」の開発競
争 → Objective-C (NeXT)、COB (日本 IBM) など• Bjane Stroustroup (ベ ル 研 究 所) が「C with

別毎の性質を「クラス」単位で記述□ 例:

フト開発にはあまり使われなかった□ C++□ クラス方式のオブジェクト指向言語…オブジェクトの種

1.3• 世界初のワークステーション (個人用計算機) である
Alto 上で稼働し、ウィンドウ、マウス、メニュー、文化もdouble x, y; ← X 座標と Y 座標

• Self --- 研究用言語、クラスを使わない (プロトタ
イプ方式)

た と え ば 移 動 と XY 座 標 の 取 得---p1.moveBy(dx, dy)、p1.getX()、p1.getY()• JavaScript --- ブラウザ上のスクリプト言語として開発され普及した。これもプロトタイプ方式

オブジェクト指向言語の歴史• Perl --- 現代的スクリプト言語の元祖。Perl はもともとはオブジェクト指向言語でないが、Perl5 で□ Simula (1960’s) --- 最初のオブジェクト指向言語

オブジェクト指向を導入 (PHP なども同様)• Python --- Perl の「後付けオブジェクト指向」に• イベントシミュレーションの記述のため、「もの」を言語上で扱うためにオブジェクト指向が発明された

対抗して最初からオブジェクト指向を組み込んだス
クリプト言語として普及• クラス、インスタンス、継承、動的分配など基本的• Ruby --- Python と同様だが「書きやすさ」指向のカルチャーに特色。日本人まつもとゆきひろ氏が開な「道具」はこの言語で既に揃っていた

2

1.4

発。日本初で世界に広まった言語というのはこれが• 「cin」「cout」は入出力ストリーム

始めて。

• 「>>」「<<」(もともとはシフト演算子) を演算子定義により入出力演算として使っている

この節のまとめ• 「>>」の結果はまたストリームなので連続して使える• 多重定義により、「<<」を文字列用、整数用、実数用□ ソフトウェア開発のあり方とプログラミング言語は関連と多数用意している• スクリプト言語系とコンパイル言語系□ 今日ではオブジェクト指向言語が広く使われている

2.2• モジュール (部品化)、継承、動的分配 (総称性)□ オブジェクト指向機能の基本要件 --- インスタンス変数とメソッド□ オブジェクト指向言語の歴史• 構造体 (struct) の拡張として class という構文を• Simula → Smalltalk → コ ン パ イ ル 系 (C++、作った。
• class ではメソッド定義もできるようにした。

Java)、スクリプト系 (Perl、PHP、Python、Ruby、
JavaScript)

2

C++言語

2.1

C++の由来とあらまし

C++のオブジェクト指向機能

struct point {double x, y;};←ここに「;」が必要!
class Point {double x, y;
public: ←以下「クラス外から見える」
Point(double x0,y0) { ←初期化
x = x0; y = y0;}
void moveBy(double dx,dy) {x += dx; y += dy;}
double getX() { return x; }double getY() { return y; }};←ここに「;」が必要!

□ Bjane Stroustrup によって、「C with class」として始まる• C からスムーズに移行できるオブジェクト指向言語として普及• C に (ほぼ) 上位互換な言語。型検査とかは厳しくなっている• +αされているもの…「オブジェクト指向」「例外」• ここではクラスの宣言に定義も入れてあるが、実際には宣言と定義を分離して記述し、宣言をヘッダファ「テンプレート」など• C の「裸のマシンがそのまま使える」に「よい構造化イルに置くができる」を追加• C++では「virtual」と指定していないメソッドは動的分配を行わない (その分効率がよい)□ C++の設計思想…

• 実際にはこのような使い方 (抽象データ型) で済む場

• フルスピードで動く (C に負けない)合も多い• そのために足枷となるものは言語に導入しない• どのようなプログラミングスタイルでもサポートする

例題:分数電卓 (C++)• プログラマが分かっていてやることは禁止しない

2.3• ユーザ定義型も組み込み型と同様な見た目で使える□ 分数をあらわすクラスを用意する。方針としては 2 つの数 a、b で分数 a/b を表す。まず宣言部。

□ 例題:

2 つの数を読み込んで足すclass Rational { // a/b
int a, b;
int gcd(int x, int y);
public:Rational(int x);
Rational(int x, int y);
Rational operator+(const Rational& r) const;
Rational operator-(const Rational& r) const;#include <iostream>using namespace std;
int main(void) {int x; cout << "x? "; cin >> x;
int y; cout << "y? "; cin >> y;cout << "x+y = " << (x+y) << ’\n’;}

3• 面倒な「約分」の計算はコンストラクタで。
Rational operator*(const Rational& r) const;
Rational operator/(const Rational& r) const;□ メイン部分。
friend ostream& operator<<(ostream& o, Rational& r);
friend istream& operator>>(istream& i, Rational& r);#include <iostream>};#include <string>#include <cstdlib>• private 部分には変数と補助関数 GCD(最大公約数)。
using namespace std;
• コンストラクタは値 1 つ (分母 1) と 2 つ。

// ここにヘッダ部分// ここに実装部分• 四則は演算子。
• 分母 0 になる場合は NaN (Not a Number)。

int main(void) {Rational r(1), x(0);while(true) {char c; cout << "? "; cin >> c;
if(c == ’q’) return 0;
switch(c) {case ’q’: return 0;case ’=’: cin>>x; r = x; break;case ’+’: cin>>x; r = r + x; break;case ’-’: cin>>x; r = r - x; break;case ’*’: cin>>x; r = r * x; break;case ’/’: cin>>x; r = r / x; break;default: continue;}
cout << r << ’\n’;}}• 入出力は単独関数として定義した演算子「>>」と「<<」。(なぜ単独関数かというと、クラス istream や
ostream に後からメソッドの追加はできないから。)
第 1 引数は入出力ストリーム。firend 宣言は「この関数は特別にこのクラス定義の中身 (private 部分)にアクセスできる」という意味。
□ 実装部分。

int Rational::gcd(int x, int y) {while(x != y) if(x > y) x -= y; else y -= x;
return x;}
Rational::Rational(int x) { a = x; b = 1; }Rational::Rational(int x, int y) {if(y == 0) { a = b = 0; return; }• 1 文字読んでコマンド文字によって動作を切替え。例:
if(x == 0) { a = 0; b = 1; return; }if(y < 0) { x = -x; y = -y; }? = 1 3 ← 1/3 を入れる
if(x == 0) {1/3
←現在値
a = x; b = y;
? + 1 6 ← 1/6 を足す} else if(x > 0) {1/2
←現在値
a = x / gcd(x,y); b = y / gcd(x,y);q←おしまい} else {a = x / gcd(-x,y); b = y / gcd(-x,y);□ ここまでで分かる特徴…
}}• C 言語を拡張してオブジェクト指向としている
Rational Rational::operator+(const Rational& r) const {return Rational(a*r.b + r.a*b, r.b*b);• 効率が悪くなる機能は使わないようにできる (さら}にメソッドに inline と指定するとその場展開→メRational Rational::operator-(const Rational& r) const {ソッド呼び出しのコストも削減可能)
return Rational(a*r.b - r.a*b, r.b*b);}• 「*」などの演算子も定義して差し替えられる
Rational Rational::operator*(const Rational& r) const {return Rational(a*r.a, r.b*b);}
この節のまとめ
Rational Rational::operator/(const Rational& r) const2.4{
return Rational(a*r.b, r.a*b);□ C++ --- C 言語に上位互換なオブジェクト指向言語}
ostream& operator<<(ostream& o, Rational& r) {□ フルスピードで動く (C に負けない) ことが目標
if(r.b == 0) o << "NaN";
else o << r.a << ’/’ << r.b;
□ ユーザ定義型も見た目で既存の型と同等に見える
return o;}istream& operator>>(istream& i, Rational& r) {int a, b; i >> a >> b;
r = Rational(a, b); return i;
}

4

3

Java 言語

3.2

3.1

Java の由来とあらまし□ オブジェクト指向言語の基本要件 --- インスタンス変数とメソッド□ Java --- 「C++よりも安全なオブジェクト指向言語」と• Java は最初から新しい言語として設計→ class という構造を作り、その中にインスタンス変数定義、メ
ソッド定義を入れる

して SunMicrosystems 社で開発された• 当初 Web ブラウザの上で動く「アプレット」のため

class Point {double x, y;
public Point(double x0,y0) {x = x0; y = y0;}
public void moveBy(double dx,dy) {x += dx; y += dy;}public double getX() { return x; }public double getY() { return y; }}←ここに「;」不要の言語として普及• この場合の「安全」…プログラムが悪さをできない
ような防壁がある (例:Java のオブジェクト指向機能

ファイルの読み書きができない、勝手にネットワーク接続ができない、など)• 現在では通常のソフトウェア開発用言語として使われている• C(や C++) とは制御構造の構文が似ているだけであとは別物• public/private 等は各宣言に付加□ Java の設計思想…

• このレベルだと C++とほどんど変わらない• C++の危険さ、複雑さを減少させ単純さを求める• C や C++との互換性は捨てたので言語仕様は単純化できた

3.3□ C++版と同じ方針で実装。こちらはメイン側から。

• 安全さを第一とし、危険なことはどうやってもできない

import java.util.*;• すべてのオブジェクトはヒープ上に割り当て、ごみ集めを行う

public class Sample32 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);
Rational r = new Rational(1);while(true) {System.out.print("? ");String s = sc.next();
if(s.equals("q")) return;int a = sc.nextInt(), b = sc.nextInt();
Rational x = new Rational(a, b);switch(s.charAt(0)) {case ’=’: r = x; break;case ’+’: r = r.add(x); break;case ’-’: r = r.sub(x); break;case ’*’: r = r.mul(x); break;case ’/’: r = r.div(x); break;default: continue;}
System.out.println(": " + r);}}}• Smalltalk 的、伝統的なオブジェクト指向言語ふう• これらの代償として見た目は長く、動作は遅くなり
がち
□ 例題:

例題:分数電卓 (Java)

2 つの数を読み込んで足す

import java.util.*;public class Sample31 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);
System.out.print("x? ");int x = sc.nextInt();
System.out.print("y? ");int y = sc.nextInt();
System.out.println("x+y = " + (x+y));}}• C++のような「演算子の駆使」はない• Scanner の next() は「次の空白等で区切られた 1 か• すべて「オブジェクト. メソッド (引数…)」で普通たまり」を返す。

に書く• 動かし方は C++版と同じ。
□ Rational クラスは内容的にはほぼ同じ。

5

class Rational {4 C++と Java の違い
int a = 0, b = 0; // b == 0 -> Not a Number
public Rational(int x) { a = x; b = 1; }4.1 プログラム構造
public Rational(int x, int y) {if(y == 0) { return; }□ C++はクラスと単独関数 (と struct と union と) があ
if(x == 0) { a = 0; b = 1; return; }る。Java はクラスのみ (と、インタフェース)。
if(y < 0) { x = -x; y = -y; }if(x == 0) {□ 分割コンパイル→ C++はやはりちょっと古い。Java はよ
a = x; b = y;} else if(x > 0) {りスマート。
a = x / gcd(x,y); b = y / gcd(x,y);} else {• C++はクラス定義+各メンバの定義。クラス定義はデー
a = x / gcd(-x,y); b = y / gcd(-x,y);タの定義+メソッドインタフェースの定義。ヘッダファ}
イルに入れる (そうしないとオブジェクトの大きさ}
public Rational add(Rational r) {が分からないので使う側がコンパイルできない)。テ
return new Rational(a*r.b + r.a*b, r.b*b);ンプレートだとさらにそういう感じ。
}
public Rational sub(Rational r) {• Java は 1 つのクラス定義に全部書く。インタフェー
return new Rational(a*r.b - r.a*b, r.b*b);
ス情報は.class ファイルに書き出され、分割コン}
パイル時はコンパイラがこれを参照する。
public Rational mul(Rational r) {return new Rational(a*r.a, r.b*b);• 名前空間の分割 (同じ名前のクラスが衝突すること}を避ける) ← Java はパッケージ機能、C++は名前空
public Rational div(Rational r) {return new Rational(a*r.b, r.a*b);
間機能。扱いは違うができることは同程度}
public String toString() {□ Java の入れ子クラス --- クラスの中にクラスが書ける
if(b == 0) return "Nan"; else return a + "/" + b;}
• static な入れ子クラス --- 普通のクラスが「外かprivate int gcd(int x, int y) {ら見えない」ように作れる (外から見せてもいい)while(x != y) if(x > y) x -= y; else y -= x;
return x;• static でない入れ子クラス (内部クラス) --- 外側}のクラスのインスタンス変数群を参照できる}• 無名内部クラス --- 内部クラスを使うのが 1 箇所だけの場合に、いちいち名前をつけなくても済む仕• 演算子定義はないので適当なメソッド名をつける。

組み• 出力も演算子はないので toString() で文字列への変換方法を定める (文字列が必要なときは自動的に• 全体として… Java では「どんどんクラスを作ってこれが呼ばれる)。

3.4

利用」→オブジェクト指向的?

この節のまとめ

4.2□ Java --- C++の問題点である、「安全でない操作もプロ

値の扱い□ 値、オブジェクト、ポインタ

グラマが分かっていてやるなら許す」ことをやめるため• C++も Java も「値」と「オブジェクト」を区別してに新規に設計いる。

□ 新しい言語として作ったため機能を整理できた (今はそ

• 値→数値、文字、論理値。単純なビット表現。

の後追加した機能があって複雑になっている面も)• オブジェクト→ C++でも Java でもクラスにより定義
し、内部的に複雑な構造を持つ (持てる)□ 書き方は標準的で平穏。その分長くなる面も• ポインタ型/参照型→ C++にのみ存在。Java ではオ
ブジェクト値は全部ポインタのようなもの (後でまた扱う)

• C++では「==」などの演算子を定義できる→オブジェ
クトでも演算子を使って書く。
6• Java では「値→演算子」「オブジェクト→メソッ
ド」ときっぱり区分されている。
「v1 == v2」(値)、「o1.equals(o2)」。もしオブジェクトに「o1 == o1」
を使うと「ポインタ比較 (同一のオブジェクトかど
うかを調べる)」になる。

• JDK 1.5 以 降 で こ れ ら を「 自 動 化 」:boxing /unboxing が必要になったときは自動的に行う (演算などの場合も)Integer i1 = 100, i2 = 200; ← auto boxing
int j1 = i1, j2 = i1+i2; ← auto unboxing
int j3 = i2++; ← auto unboxing+boxing□ Java の包囲クラス• C++では文字列→数値変換などは (C に由来する)atoiなどの関数利用。
• Java で は そ の よ う な も の の 置 き 場 所 と し て「Integer」「Double」などのクラス (包囲クラス、
wrapper class) がある
int x = Integer.parseInt("123");• これらのクラスは int や double の値を「オブジェクトにくるんで扱う」ためにも使われる (包囲クラスという名前の由来)
Integer i = new Integer(123);
int k = i.intValue();• なぜクラスにするかというと、Java では「オブジェクトでないと不便なこと」が色々ある (例: Object型の変数にはオブジェクトなら何でも入れられるが、値はオブジェクトでないので入れられない• C++は (テンプレート機構を使って) オブジェクトで包囲クラスは不要□ すべての式や変数に型がある→それらに「不一致」があ• 数値型など基本型の間では一定範囲で「自動変換」が提供されている• オブジェクト型どうしでは「上位の変数に下位のオブジェクトが入れられる」という規則が「原則」• 便利さのためのこの「原則」をやめて変換を可能とする場合がある• 代入時も別扱いしたければ代入演算子も定義• 基本型や既にあるクラスを「変換先」にしたい…コンストラクタは定義できない→その場合は「型変換演算子」を定義 (「operator T()」…自分クラスの値を T 型に変換)
int i = x; // x は C1 型...
class C1 {public:operator int() { return this.size; }...
}□ Java の AutoBoxing/Unboxing• Java では「1」(値) と「new Integer(1)」のように基本型とそれに対応するオブジェクト型の両方を用意→相互に変換することが多い

オブジェクトから

public class Sample41 {public static void main(String[] args) {// int k = 0;
Integer k = 0;
for(int i = 0; i < 1000000000; ++i)k = k + i;}}

C1 x = 5; // C1 x = C1(5) と同じ• オブジェクトと基本型→相互変換行わないのが「原則」

する• Unboxing --- 「箱から出す」:
値を取り出す• メソッド呼び出しのために autoboxing されることはない。「3.toString()」はダメ• 整数だと思っているのが実は Integer オブジェクトだったりすると、見た目で感じるのとまったく違う
オーバヘッドが掛かることがある→「どの型なのか」をきちんと把握しておかないと危険class C1 {public:C1(&int i) { ... }
...
}

ることも

値をオブジェクトにことに注意• 多くの場合「変換」しなくても演算子オーバロード
で済む• コンストラクタを定義することで自動型変換が可能になる

型情報を利用した自動型変換• Boxing --- 「箱に入れる」:• しかし「==」「!=」はオブジェクトどうしが等しいという意味がもともとあるので auto unbox されない

□ C++の自動変換機能

も値でも見た目は変わり無く扱えるようにするので

4.3

Integer i1 = new Integer(1); ← boxing
int j1 = i1.intValue();← unboxing• これらの複数の機能のどれを使うか→言語仕様で規定 (複雑)

7

#include <iostream>using namespace std;

• C++ではコンパイル時に多重定義のどれが使われるか

class Test {int val;public:Test() { val = 9; }//Test(int i) { val = i+1; }//void operator=(const int& i) { val = i+2; }//operator int() const { return val+3; }int value() { return val; }};• この「実行時の Java」「コンパイル時の C++」といを決定→コンパイル時にすべて処理される (高速)
う違いは言語の設計思想の違いによっている□ 親クラスの変数に入れたものを「元に戻す」必要性
Animal a = new Dog(...) // or Cat...
Dog d = (Dog)a;
Animal *a = new Dog(...) // or Cat...
Dog *d = dynamic_cast<Dog>(a);int main() {Test x;//Test x = 10;//x = 20;cout << x.value() << "\n";//cout << (x+0) << "\n";}4.4• a に入っているものが Dog なら Dog に戻せるが、Cat
なら Dog に戻せてはいけない→エラーになる• このために型の情報が実行時に保持されている必要□ Java の場合 (上の例)

動的な型の扱い• すべてのオブジェクトにはクラス情報が付随□ たとえば、「何でも出力しちゃう関数」を作る:• オブジェクトに対するキャストはクラス情報により• Java の場合→「Object 型の変数には任意のオブジェ
クトが入れられる」(Object はすべてのクラスの祖
先だから) ことを利用 + AutoBoxingチェック• ほかに「if(a instanceof Cat) ...」などの演算子もある

public class Sample42 {public static void main(String[] args) {p(1);p("abc");p(new int[]{1, 2, 3});}
public static void p(Object o) {System.out.println(o); ←文字列変換は動的分配}}• もっと自由に型の情報そのものを扱う→自己反映機
能 (後述)□ C++の場合 (下の例)• クラス情報が付随するのは「virtural メソッド (動的分配を行うメソッド) を持つクラスのオブジェク
ト」のみ←効率のため• C++の場合→テンプレート機能を使って任意の型 T を受け取り、それを総称的に扱うことができることを利用• そのようなオブジェクトに限り dynamic cast が使える#include <iostream>using namespace std;

• それ以外のキャスト (型変換) はコンパイル時の扱い
のみ

template<typename T> void p(T x) {cout << x << "\n"; ←「<<」の多重定義を利用}
int main() {p(1);p("abc");int a[] = {1, 2, 3};p(a);}• 実 行 時 に 情 報 が な い 場 合 の キャス ト →static cast<T>(値)• constの あ る 型 か ら

const

を 外 す →

const cast<T>(値)• 何 を 何 に で も 変 換 で き る キャス ト →reinterpret cast<T>(値) ← こ れ ま で の「(型)値」というキャストと同等。これまでのキャストは□ 一見似ているようだが全然違う

危険な上に目立たないのでやめさせたいという考え• Java では実行時の動的分配を用いて各オブジェクト毎の処理をする→コンパイル時に存在していないク
ラスでも OK(柔軟性)

8

4.5

多重継承とインタフェース

abstract class Drawable {public:void draw(Graphics g) = 0;};

□ 多重継承 --- あるクラスが複数の親クラスを持つこと• 用途 1:

1 つのオブジェクトがいくつかの側面を併せ持つ場合。たとえば「馬」は「交通手段」の一種

4.6であるし「動物」の一種でもある。

• 用途 2:

記憶域の割り当てあるオブジェクトを実装するのに複数のオ □ C++と Java で一番違うところは:ブジェクトの機能を取り込んで来たい場合。←干渉• C++ではオブジェクトは「配列や実行スタック上に直接その記憶域を配置できる。外に配置するときはポなどの問題がある…

インタを使う」□ 継承そのものが「総称的に扱う手段を提供 (用途 1)」と「実装を取り込んで来て短く記述 (用途 2)」の 2 側面を• Java ではオブジェクトは「すべてヒープ上のオブジェ持つ

クト。オブジェクトはすべてポインタで扱う」(Javaの用語では「参照」) →最も重要な単純化。動的デー□ C++ --- 多重継承を実装

タ構造や多態のためにはどのみち参照が必要。その• 1 つのクラスに複数の親クラスを指定できる。そのため、すべてポインタに統一している。そのためにクラスの実体は複数の親クラスのデータを連結した遅い面も。

ものに独自のデータを付加した形になる (一部例外class point {double x, y;
... }

あり)

• 干渉の問題は解決されていない (プログラマの責任)p
C++□ Java --- 多重継承は除外し、代わりにインタフェース

p

x
y

Java

機能

r

interface Drawable {public void draw(Graphics g); ←画面に描ける
}

C++

x
y
x

• 1 つのクラスは任意個数のインタフェースに従うことができる• インタフェース型の変数にはそのインタフェースに従うクラスのインスタンスであればどれでも入れることができる (メソッド呼び出し時に動的分配)yC++class Rect {Point p1, p2;... }• インタフェース --- オブジェクトが持つメソッド名とその引数や返値の情報 (メソッドシグニチャ) を集
めたもの

x

r

x
Java

y

C++

y

x
y

• そのため、Java ではごみ集め (GC、後述) が必須。
C++ではごみ集めはオーバヘッドがあるので言語本体としては提供しない→手動メモリ管理が前提 (うまclass Cirlce implements Drawable { ... }Drawable d = new Circle(...);...
d.draw(g);

くやれば効率はよいが面倒、間違えるとメモリリー
クしたり落ちたりする)。

□ 両者を比較すると…

□ C++でのオブジェクトの初期設定と後始末は…

• Java は「用途 1」が主な目的、C++は「用途 2」が (ある程度) 目的• コンストラクタとデストラクタ (名前が「〜クラス名」) による• プログラムの構造化という点では「用途 1」が重要。
少なくとも「用途 1」と「用途 2」が分離できること• ローカル変数のオブジェクトはスコープに入った時にコンストラクタが呼ばれ、出るときにデストラクが重要→ Java の方が新しい言語だからそれなり• C++でインタフェースと同じことをする場合は、「変数もメソッド内容も定義しない抽象クラス」を作成する

タが呼ばれる。

• ヒープ上のものは「new クラス名 (…)」でメモリ割
り当て+コンストラクタ、「delete オブジェクト」でデストラクタ+メモリ開放

9• 配列の場合「new クラス名 [大きさ]」で割り当て+• GC のオーバヘッドは織り込み済みという割り切りがデフォルトコンストラクタ (引数なし)、「delete[]必要。

ポインタ」でデストラクタ+メモリ開放。(ポインタが単一要素か配列参照か分からないという弱点は C• 自分で領域開放するよりずっと楽。自前でやるとコードが複雑化し、また重大な誤りの原因になりやすい。

言語から引き継いでいる。)• GC は Lisp あたりでは古くから使われているが、普通の手続き型言語で標準としたのは Java がはじめて□ Java の場合は「new クラス名 (…)」でコンストラクタというのは C++と同じ。配列はポインタ配列なので初期□ GC の原理:

値 null。領域回収は GC なのでずっと簡単。後始末用

たどって行き、たどったという印をつける。印がつかなにはメソッド finalize()、ただし GC が領域を回収す

かった領域はごみとして回収るときに呼ばれるので、呼ばれない場合も。
scope
entry

Ｃ＋＋
（直接）

Ｃ＋＋
（ヒープ）

Ｊａｖａ
常にヒープ

{ Obj x;{ Obj *x =new Obj();{ Obj x =new Obj();

…

…

…

…

…

…

}

delete *x;• その他、使っているオブジェクトだけコピーする方
式、オブジェクトがそれぞれ何個所から指されているか常に数えておく方式などもある。

• GC で自動的に回収するためには「不要なデータ構造
は指さないようにする」(指している所に null を入れるなどしてつながりを切る) ことが必要}}• 「もう要らないからこれ回収して」とは言えない (安
全のため)

scope
exit
scope
entry

scope
exit

自動的に
コンスト
ラクタ
呼出

明示的に
コンスト
ラクタ
呼出

自動的に
デスト
ラクタ
呼出

明示的に
デスト
ラクタ
呼出（必須）

Ｃ＋＋
Obj::Obj() {...
}Obj::~Obj() {
...
}

明示的に
コンスト
ラクタ
呼出

4.8

何もしない
（ＧＣ時にあれば後始末を呼出）Ｊａｖａclass Obj {public Obj() {
...
}
...

class IntSet {int[] arr;
int count = 0;
public IntSet() { arr = new int[0]; }private IntSet(int c) { arr = new int[c]; }private void add1(int x) {if(count+1 >= arr.length) {int[] a = new int[arr.length*2 + 1];
for(int i=0; i<count; ++i) a[i] = arr[i];arr = a;}arr[count++] = x;
}
(以下略)
}

public void finalize() {...
}}ているにもかかわらず、継承 (とオブジェクト指向) を使う場合はヒープ割り当てしてポインタアクセスするしかない!
スタック上のオブジェクトは「大きさ固定」

だからサブクラスでインスタンス変数を増やしたりしても対応できない。無理にスタック上に取っても• Java では配列は常に「別オブジェクトへの参照」に格納するときに「余分な変数が切捨て」変換されてなる

しまい役に立たない。

• 必要な容量を見積もってその大きさで用意し、容量が不足したら大きいものを用意して差し替え。

• だったら Java 方式でいいじゃん!

4.7

C++のコピーコンストラクタと代入演算子□ 「整数の集合」を Java で実装→実際の並びは配列に格納→値を追加していくと最初に用意した配列では不足→「配列が足りなくなったら大きくする」□ C++ではこれだけ「スタック上に直接」のために努力し• 理由:

変数から参照できるオブジェクト群を順次• 使わなくなった古い配列はごみ集めにより回収

Java とごみ集め□ 同じものを C++で実装するとどうか?

□ Java のコード→どんどん必要なオブジェクトを生成し、使わないものはごみ集め (Garbage Collection、GC)
で回収、というスタイル。

10

Intset::Intset(int c) {count = 0; limit = c; arr = new int[c];}□ 前問の答え:

Intset::Intset() {count = limit = 0; arr = new int[0];}Intset::~Intset() { delete[] arr; }void Intset::add1(int x) {if(count+1 >= limit) {int *a = new int[limit*2+1];
for(int i=0; i<count; ++i) a[i] = arr[i];delete[] arr; arr = a;limit = limit*2+1;}
arr[count++] = x;}

いずれも「初期化」 。引数は実引数のコピーで初期化される。返値は返値用のテンポラリがreturn の式で初期化される。
□ 「初期化」も「代入」も内部データ構造に応じたものを用意しないとまずい*場合がある*→「コピーコンストラ
クタ」と「代入演算子」
Intset(const Intset &s);
Intset& operator=(const Intset& s);• 指定しない場合は「各メンバごとのコピー」が用意される。それだとどういう場合にまずいか分かりますか?

• 初期化時に配列サイズを決めて割り当てる。

• デストラクタが必要に (このオブジェクトが不要に □ 通常のコピー (引数渡し等)、代入動作→オブジェクトのそれぞれのインスタンス変数をコピー。
なったら指している配列も不要になるから。
• add1() で配列が満杯になったら増やす。

• 変数 arr をコピーする→ポインタのコピーであって配列はコピーされない (共有された状態になる) →そ□ クラス定義部分のコードを示す:

れでは困る (副作用とか)。

class Intset { // set of ints
int count, limit;
int *arr;
Intset(int c);void add1(int x);
static int min(int x, int y) { return (x<y)?x:y; }public:Intset();←空の並びを作る
Intset(const Intset &s); ←？？？
~Intset();←デストラクタ
Intset& operator=(const Intset& s); ←？？？
int size() const;←サイズ
bool is_in(const int i) const; ←包含判定
Intset operator+(const Intset &s) const;
Intset operator-(const Intset &s) const;
Intset operator*(const Intset &s) const;
friend ostream& operator<<(ostream& o, Intset& s);friend istream& operator>>(istream& i, Intset& s);};• このため、「コピーコンストラクタ」と「代入演算子」
を作成して、その中で arr が指す配列をコピーする。
Intset y = x;
何もしない
場合
起こって
欲しいこと

y

x
3

5

3

3

3

5

y
1 3 7
初期化
1 3 7

y = z;
何もしない
場合

y

z
2

5

？
起こって
欲しいこと

2
1 3 7

5
8 9

y
2

3
代入
8 9

• ヘンなコンストラクタと演算子が増えている?

□ 具体的なコード (この種のものとしては典型的)□ QUIZ: あるクラス Someclass の変数 x、y があるとする。
□ 次の 2 つは同等だと思う?
(A) Someclass x = y;

□ 前問の答え:

YES/NO(B) Somclass x;
x = y;「初期化」と「代入」は別物• 初期化は「ゼロから内部データ構造を作る」• 代入は「できているデータ構造に格納する」□ 次の場合は初期化か代入か?
(A) int f(Someclass x) (B) Someclass f() {...
...
f(y) ←呼び出し
return y; ←返値}

Intset::Intset(const Intset &s) {arr = new int[s.count];
limit = count = s.count;for(int i = 0; i < count; ++i)
arr[i] = s.arr[i];}Intset& Intset::operator=(const Intset& s) {if(this != &s) {count = 0;
for(int i = 0; i < s.count; ++i)
add1(s.arr[i]);}return *this;}• 代入の場合は「自分への代入は何もしない」を入れておくのが通例。

11

4.9

本節のまとめ• Java ではこれらをきちんと型を割り当てた上で可能にしている□ 設計思想として、Java はオブジェクトを多数使って、動• ある意味では、Lisp 等の「eval」(任意のプログラ的に (実行時に) さまざまな処理を行う。C++はできるだ

ムを合成してその場で走らせる」を強い型の言語上

けコンパイル時に解決することで、効率良く実行できるで可能にしたといえることを重視。

□ Java は実行時に何でもできるようにする分、遅い面も。 □ 任意のオブジェクトは getClass() でその Class オブ
ジェクトを取得できる□ C++は実行時の効率重視であるため、実行時にできることは少ない。

• または、「型名.class」でも Class オブジェクトを指定できる□ C++は効率重視のため色々選べるがそのため複雑になっている面も。

□ Class オブジェクトはそのクラスに関する情報を取得す□ コンパイル時の参照方式やインタフェースなど、Java のるメソッドを持つ

方が新しい分スマート。また、Java の方が安全。

• 例: getConstractors(), getMethods(),
getMembers()□ C++は「プログラマが分かっていて使う」言語。分かっていれば何でもやっていいが、相応の責任。
□ Java は「安全側にチェックしつつ動く」言語。危ないこ□ Constructor オブジェクトの newInstance() を呼ぶとオブジェクトが生成されるとはできなくしてあり、安心だがのろい。

□ Method オブジェクトの invoke() を呼ぶとメソッドが□ あなたなら、どちらが好みですか?

5

実行時の動的処理と注記

5.1

Java のリフレクション機能□ 自己反映 (reflection):

実行できる□ たとえば、任意のクラスを 1 つもってきてオブジェクトを生成しメソッドを呼ぶ (ただし引数はすべて空) というプログラムimport java.util.*;import java.lang.reflect.*;

実行中のコードが、実行系の情報にアクセスしたり、実行系の状態/動作を変更したりできるような機能• 狭い意味では前者のみ (後者を reification と呼んで区別することも)□ 何のためにそんなことをするのか?

• 例: デバッガ→実行系の内部状態を調べたり変更する必要• 例: システムの拡張→「任意の手続き呼び出しを遠隔メッセージに変換」など• 例:

拡張可能言語 (構文や意味づけ等)□ Java の自己反映機能→処理系そのもを変更する、という部分はない。

• 内部の状態をのぞく• のぞいた情報を利用して、その場でメソッド呼び出し等を組み立てて実行させられる□ 強い型の言語は「型が合わなければ扱えない」→リフレ
クションのような自由自在なことは表しにくい

12

public class Sample51 {public static void main(String args[]) {Scanner sc = new Scanner(System.in);while(true) {try {System.out.print("Class Name? ");String cname = sc.nextLine();if(cname.equals("q")) break;Class cls = Class.forName(cname);
Constructor[] cons = cls.getConstructors();for(int i = 0; i < cons.length; ++i)
System.out.println(""+i+": "+cons[i]);System.out.print("Constructor Number? ");int cno = sc.nextInt();
Object obj =cons[cno].newInstance(new Object[]{});
Method[] meths = cls.getMethods();for(int i = 0; i < meths.length; ++i)
System.out.println(""+i+": "+meths[i]);System.out.print("Method Number? ");int mno = sc.nextInt(); sc.nextLine();Object res = meths[mno].invoke(obj, new Object[]{});
System.out.println("Result Class:"+ (res.getClass()));
System.out.println("Result: "+res);} catch(Exception e) { e.printStackTrace(); }}}}□ これをたとえば次のクラスに対して使ってみる…
public class Sample51Test {int val;public Sample51Test() { val = 1; }public Sample51Test(int i) { val = i; }public Sample51Test add() {return new Sample51Test(val+1);}
public Sample51Test sub() {return new Sample51Test(val-1);}
public String toString() {return "Sample51Test("+val+")";}}□ なお、この方法で通常取れるのは public なものだけ (セキュリティ上の制約)

リフレクション機能の使用例

5.2
□ 例:

コンポーネントツールや GUI ビルダ• 画面で直接部品を配置したり動かしてみたりしたい• 動かせるためには「部品そのもの」を操作したい• しかし、部品オブジェクトは部品ごとに「使い方」が異なる• →自己反映機能を用いて調べつつ「呼び出せば」よい□ Java による GUI 部品配置の例
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
public class Sample52 extends JFrame {int x1, y1, x2, y2, x0, y0, width, height;
Component c0 = null;
JPanel p0 = new JPanel() {public void paint(Graphics g) {g.drawRect(x0, y0, width, height); } };Label l0 = new Label("Component:");TextField t0 = new TextField();
Button b0 = new Button("Create");Button b1 = new Button("Move");Label l1 = new Label();Choice c2 = new Choice();Label l2 = new Label("String:");TextField t2 = new TextField();
Button b2 = new Button("Call");
Method[] meths; int[] maps;
public Sample52() {getContentPane().add(p0); pack();
p0.setLayout(null);
p0.add(l0); l0.setBounds(10, 10, 60, 30);
p0.add(t0); t0.setBounds(80, 10, 200, 30);13

p0.add(b0); b0.setBounds(290, 10, 60, 30);
p0.add(b1); b1.setBounds(360, 10, 40, 30);
p0.add(l1); l1.setBounds(10, 40, 380, 30);
p0.add(c2); c2.setBounds(10, 70, 400, 30);
p0.add(l2); l2.setBounds(10, 110, 60, 30);
p0.add(t2); t2.setBounds(80, 110, 200, 30);
p0.add(b2); b2.setBounds(290, 110, 40, 30);b0.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {try {c0 = (Component)Class.forName(t0.getText()).newInstance(
p0.add(c0);c0.setBounds(x0, y0, width, height);
t0.setText("");
meths = c0.getClass().getMethods();
maps = new int[meths.length];c2.removeAll();for(int i=0,k=0; i < meths.length; ++i) {Class[] arg = meths[i].getParameterTypes();if(arg.length == 1 && arg[0] == String.class) {c2.add(meths[i].toString());
maps[k++] = i;}}
} catch(Exception ex) {l1.setText(ex.toString());}}});
b1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {if(c0 != null) {c0.setBounds(x0, y0, width, height);}}});
b2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {try {Method m = meths[maps[c2.getSelectedIndex()]];
m.invoke(c0, new Object[]{t2.getText()});
t2.setText("");} catch(Exception ex) {l1.setText(ex.toString());}}});
p0.addMouseListener(new MouseAdapter() {public void mousePressed(MouseEvent e) {x1 = x2 = e.getX(); y1 = y2 = e.getY();
calcbox(); repaint();}
public void mouseReleased(MouseEvent e) {x2 = e.getX(); y2 = e.getY();
calcbox(); repaint();}});
p0.addMouseMotionListener(new MouseMotionAdapter() {public void mouseDragged(MouseEvent e) {x2 = e.getX(); y2 = e.getY();
calcbox(); repaint();}• メソッドを定義すると、そのメソッド名で値を保持
できる

});
}
private void calcbox() {x0 = Math.min(x1, x2); y0 = Math.min(y1, y2);width = Math.abs(x1-x2); height = Math.abs(y1-y2);}
public static void main(String[] args) {Sample52 app = new Sample52();
app.setDefaultCloseOperation(EXIT_ON_CLOSE);
app.setSize(600, 400); app.setVisible(true);}@interface Marked {String name(); ← name という文字列とint value(); ← value という数値を保持}• アノテーション定義の前につけるアノテーション (メタアノテーション) で「いつまで残すか」などを指定できる (コンパイル時、class ファイルに残す、実行時まで残す)}@Retention(RetentionPolicy.RUNTIME)

5.3□ クラス、メソッド、変数の前にアノテーションを指定

注記 (アノテーション)• 上記種別の「どれに」指定できるかもメタアノテー□ 動的にクラスやオブジェクトを取り出して操作 (リフレションで指定可能クション) → それらのクラスや要素 (メソッド等) に関□ 指定方法…

する情報も参照したい• リフレクションで取れる情報:
チャ情報などに限定• 何もメソッドがないアノテーションは「@注記名」だ

メソッド名やシグニ

け指定• メソッドがある場合は「@注記名 (名前=値, 名前=値,• 「このメソッドについてはこれこれ」というツール/...)」で指定

アプリ固有の情報を付属させたい• クラスについては、「何もメソッド定義しないインタフ
ェース」を使って「印つけ」できる --- 例:□ 利用方法…

class• ク ラ ス オ ブ ジェク ト、メ ソッド オ ブ ジェク ト 等 に
getAnnotation() というメソッドがあり、これにア

X implements Serializable• それ以外はこれまでは「コメントに書く」「それをツー

ノテーション型 (インタフェースに対応したクラスオ

ルで処理」くらいしか方法がなかった→繁雑、標準

ブジェクト) を渡すと対応する型のアノテーション

化されてない、チェックされないが (付加されていれば) 取得できる□ JDK 1.5 から注記 (アノテーション) 機能を導入
import java.lang.annotation.*;import java.lang.reflect.*;• クラス、メソッド、変数の前に「これはこうね」という付加情報を書ける@Retention(RetentionPolicy.RUNTIME)• 付加情報の内容や扱い (実行時まで残すか等) をユー @interface Marked {String name();
ザが制御可能int value();• 標準の注記もいくつか。@Deprecated (もう古いか }ら使わないこと)、@Override など。コンパイラが処理。
class Test {String str;
public Test(String n) { name = n; }@Marked(name = "abc", value = 123)
public void m1() { System.out.println("m1:" + str); }public void m2() { System.out.println("m2:" + str); }public void m3() { System.out.println("m3:" + str); }@Marked(name = "xyz", value = 456)
public void m4() { System.out.println("m4:" + str); }@Override
public void paint(Graphics g)...

• とくに @Override (このメソッドは他のメソッドを差し替えてます) はこれまでの「メソッド名をミスタイプしたため差し替えが効いてない」というバグの防止に有効@Override
public void panit(Graphics g)... ←警告!

}

public class Sample53 {public static void main(String[] args) throws Exception {Test t = new Test("Me");• インタフェース定義の形をしているが「@interface」
Class cls = t.getClass();
ではじまる
Method[] meths = cls.getMethods();□ アノテーションは自分で定義できる。

14

6.2

for(Method m: meths) {Marked a = m.getAnnotation(Marked.class);if(a != null) {System.out.println(a.name() + a.value());
m.invoke(t, new Object[]{});}}

コンテナクラス□ C++でも Java でも配列は「個数を最初に指定」→途中
で大きさが伸び縮みできるものが欲しい→ライブラリで用意。
□ JDK 1.4 までの Java では → 例: ArrayList クラス。
任意のオブジェクトを格納できる配列。

}}

import java.util.*;

5.4

public class Sample61 {public static void main(String[] args) {ArrayList a = new ArrayList(); //サイズ 0
for(int i = 0; i < 10; ++i)a.add("X" + i); // 文字列を追加
for(int i = 0; i < a.size(); ++i) {String s = (String)a.get(i); // 取出し
System.out.println(s);}}}

本節のまとめ□ 自己反映機能→「プログラムの中で、プログラムそのものを操作できる」→より柔軟なソフトウェア構成が可能になるが、その反面、プログラムが分かりにくくなる□ どこまで「静的」(コンパイル時)、どこから「動的」(実行時) の線引きをきちんと考える必要はあるが、動的にもできることは Java の利点□ アノテーション機能→動的に扱う時に付加情報をアド• 何か疑問な点はありますか?

ホックでない方法でコードそのものに入れておける→さ• この方法の弱点は何だと思う?

まざまな用途に利用可能□ ダウンキャスト … 「元の型にキャストし戻す」こと。

6型パラメタと総称的プログラミング

6.1

型パラメタ

• a.add("abc") --- Object 型のパラメタに String
を渡す→ OK• String s = a.get(i) --- Object 型は String 型に入れられない。×。

□ 配列型→「int の配列」「char の配列」など型をパラメ• String s = (Object)a.get(i) --- ダウンキャス

タに持てるト:親クラスの型から子クラスの型へのキャスト。こ• 見かたを変えれば…1 つの「もの」が複数の型に対し利用できる←「総称的」(generic) というのとき「元々なに型だったか」がチェックされる。

String でなかったものは String にはキャストできない (ClassCastException が投げられる)。

• 組み込み演算子なども総称的。5 / 2 == 2, 5.0 /2.0 = 2.5。
• 総 称 関 数 … max(int,int)→
int,max(double,double) → double 等 。← オ ー□ 弱点 (まずいこと) のまとめ

バローディングである程度はできるが、有限個数• a.

ダウンキャストは繁雑。

• b. 実行時にチェックが必要→オーバヘッドになる(Java はそれを気にしない言語だということはある• array[int]、array[double] …型がパラメタになっている (型パラメタ) →無限の場合に対応。

が) 。

• c. オブジェクトでないと入れられない。たとえば
int は Integer クラスのインスタンスにして入れな□ 「さまざまな型の配列」は古くからあるが…

• 今日の言語では、後から作成するライブラリでも同様のことがやりたい (予め用意されているものとの間の「区別」をなくしたい)• パラメタつきの型「自前で」(ユーザ定義で) 作れる

ければならない (これもオーバヘッド)。

• d. 配列だから「均一なものの並び」にしたいのに、何でも入ってしまう。
• e.

ようにするには?

間違ってヘンなものを入れてもコンパイラは怒ってくれない。

• C++ → テンプレート機能 (わりと前からある)• f. そのヘンなものが取り出されて来たところで実行時にエラー。

• Java → Generics 機能 (JDK 1.5 から)
15□ 実行してみないとエラーが出ないというのはコンパイル
int main() {vector<int> a;for(int i = 0; i < 10; ++i)a.push_back(i+9);for(int i = 0; i < a.size(); ++i)
cout << a[i] << ’\n’; // operator[]}する言語にとっての敗北。
□ 根本的な問題…本来「型パラメタ」によって複数の型 (クラス) を使用するべきところを 1 個の ArrayList クラ
スで済ませていること。
□ JDK 1.5 から「型パラメタ」が使えるようになった (JavaGenerics)。ArrayList<T>のようにパラメタは「<>」の中に指定する。T のところには任意の型 (ただしクラス)• C++では型パラメタをソースのその位置に埋め込んで全体をコンパイルするという実装 (非均一な実装、を入れてよい。

heterogenous implementation)。だから型パラメ□ 動かすときは…当然、JDK 1.5 以降の javac を使う。逆

タごとに別のコードができる。その代わり、その場に 1.5 の javac で 1.4 のソースを動かすときはオプショ展開ができる→極めて高速。

ン指定必要• javac -source 1.4 Sample31.java ←ソース• javac Sample31b.java ←(Generics)

JDK1.5

JDK1.46.3

型パラメタと継承関係□ X ⊃ Y で継承関係を表すものとする。たとえば Object

ソ ー ス⊃ String。

import java.util.*;□ パラメタつきクラス C<T>を考える。

public class Sample61b {□ クイズ: X ⊃ Y ならば、C<X>⊃ C<Y> だといえるか?
public static void main(String[] args) {ArrayList<String> a = new ArrayList<String>();
• た
と
え
ば
、ArrayList<Object> ⊃for(int i = 0; i < 10; ++i)
ArrayList<String> か? YES/NO?
a.add("X" + i);
for(int i = 0; i < a.size(); ++i) {□ 次のように考えるべきString s = a.get(i);System.out.println(s);• X ⊃ Y とは、X 型オブジェクトの代わりに Y 型オブ}}ジェクトを使っても OK であること (互換性がある}こと)• これで先の弱点のうち a、b、e、f が解消。残る c はダメ。

• 例: 「自動車」クラスの変数に「乗用車」を格納しても OK(互換性がある)• 実はこのコードを実行するとき内部的には先と同様の□ ArrayList<Object> a = new ArrayList<String>;
コードが使われている。つまり ArrayList の実装は 1
は OK?
つ (均一な実装、homogeneous implementation)。
だから c の制約が残っている。
• a.get(0) ... String が取り出されるから OK。
• c の 緩 和 策 と し て 、JDK 1.5 で int と• a.set(0, Z) ... Object が入れられないと困る
Integer、double と Double 等 の 自 動 変 換が String しか駄目!!!
(AutoBoxing/Unboxing) を追加。
ArrayList<Integer> a = ...
a.add(3); // JDK1.5 で○...
int i = a.get(0); // 〃□ よって先の質問の答えは「NO」であるべきなんだけど…
□ 実は配列でも同じことが起こる。

□ C++では→言語設計上のポリシーとして、c は受け入れられない。また、C++では配列は「直接そのオブジェク
トが埋め込まれた」ものになるので、コンテナクラスも同様でなければ受け入れられない。←フルスピードで動く C に負けないことが原則。
//sample61#include <vector>#include <iostream>□ しかし!

Java では X ⊃ Y ならば X[] ⊃ Y[] になっている• 「X[] a = new Y[10];」が可能。そのあとで「a[0]= new X();」→ ArrayStoreException 発生□ 一 般 に こ う い う の を contravariance/covarianceproblem というらしい

16

6.4

Java Generics の制約

for(Iterator<T>i = a.iterator(); i.hasNext(); ) {T x = i.next();...
}□ Java Generics では実行時には型パラメタ情報がなくなっている→そのため、型検査の抜け道が起きる可能性→それを防ぐための制約

for(T x : a) {...
}• たとえば、「パラメタ型の配列は作れない」「パラメ
タつき型の配列は作れない」□ C++では共通のインタフェースはない (各コンテナごとに違う) が形としては次のもの。

ArrayList<String>[] lsa = new ArrayList<String>[5];// ↑これは本来は許されない
Object[] oa = (Object[])lsa; // Java では OKoa[0] = new ArrayList<Integer>(); // !!!
...
String s = lsa[0].get(0); // 実行時例外!!!

6.5*p --- 現在の要素を参照++p、p++ --- 次の要素に進める
p == q --- 同じ要素を指すかどうか判定//sample61b#include <vector>#include <iostream>

イテレータ

int main() {vector<int> a;for(int i = 0; i < 10; ++i)a.push_back(i+9);for(vector<int>::iterator i = a.begin(); i != a.end(); )cout << *i++ << ’\n’;}□ コ ン テ ナ の 種 類 → 可 変 長 配 列 (ArrayList<T>、vector<T>)、連 結 リ ス ト、B 木 、ハッシュ表 、などなど。
• どれでも「順番に要素を処理する」などの操作は必要→それをどれでも同等に扱えるようにしたい (後• 「vectior<int>::iterator」→ vector<int>クラスにおいて typedef で定義されている iterator とでコンテナを取り替えたりしても利用コードは変えないで済むように)。

いう型名。

• 「イテレータ (iterator、反復子)」→次々に要素に• 普通の「配列要素を指すポインタ」もイテレータとして使えるアクセスしていくためのオブジェクト。
□ Java では共通の Iterator インタフェースとして規定。
Iterator<T>のように各要素の型が型パラメタになる。

• コンテナにもよるが単なるポインタではないイテレー
タが大半

bool hasNext() --- 終わりかどうか調べる
T next() --- 次の値 (T:パラメタ型) を取り出す
void remove() --- 現在値を削除 (今回は使わない)
import java.util.*;

6.6
□ 例:

型パラメタつきクラスを作る
リングバッファ。次々に値を追加できるが、最近の N 個だけが記憶されている。(実際には 2 のべき乗個

public class Sample61c {public static void main(String[] args) {を記憶→計算が簡単だから)
ArrayList<String> a = new ArrayList<String>();□ C++版 (クラス定義の中に直接コード本体も書いている。
for(int i = 0; i < 10; ++i)
こうするとインライン展開が可能に。または従来通り分
a.add("X" + i);
けて書いてもいいが、その場合は inline と指定。)
for(Iterator<String>i = a.iterator(); i.hasNext(); ) {String s = i.next();//sample62
System.out.println(s);#include <iostream>}}
template<typename T> class ring_iter {}const int mask;
const T *arr;
int index;□ JDK 1.5 から、上のような長い for 文を書かなくても済
public:むように。
ring_iter(const int m, const T *const a, const int i): mask(m), arr(a), index(i) { }• 式 a の型がインタフェース Iterable<T> に従って
const T& operator*() const {いるなら、次の 2 つは同等。
return arr[index];

17

mask = m; size = s;arr = new ArrayList<T>(mask+1);
for(int i = 0; i < mask+1; ++i) arr.add(null);}bool operator!=(const ring_iter &i) const {return index != i.index;}
ring_iter operator++(int dummy) {index = (index-1)&mask; return *this;}int geti() const { return index; }};}public void add(T v) {arr.set(mask & (count++), v);}
public Iterator<T> iterator() {return new MyIter(count&mask, (count-size)&mask);}
class MyIter implements Iterator<T> {int index, end;
public MyIter(int i, int e) {index = i; end = e;}public boolean hasNext() {return index != end;}
public T next() {index = (index-1) & mask;
return arr.get(index);}
public void remove() {throw new UnsupportedOperationException();}}template<typename T> class ring {static const int mask = 0x0f;
static const int size = 10;
T arr[mask+1];
int count;public:typedef ring_iter<T> iterator;ring() { count = 0; }void push_back(T v) {arr[mask & (count++)] = v;}
iterator begin() {return ring_iter<T>(mask, arr, count&mask);}
iterator end() {return ring_iter<T>(mask, arr, (count-size)&mask);}};}□ C++と Java の比較…
int main() {ring<int> a;• C++はテンプレートによる型パラメタの実装が古くか
for(int i = 0; i < 100; ++i)
らあるのでこなれている。高速。ただし美しくない
a.push_back(i+9);部分も。
for(ring<int>::iterator i = a.begin(); i != a.end();)
cout << *i++ << ’\n’;• Java は Generics を後づけで入れたのでこなれてい}ない部分もある。一方で新しい設計ならではの見やすさもある (内部クラスや for)。

□ Java 版。パラメタ型の配列を生成することはできないという制約があるため、ArrayList<T>を使っている。

• イテレータをコンテナの内部クラスにすることでいちいち内部データ構造を渡して初期化しなくて済む(直接参照できる)

6.7□ C++の標準ライブラリの土台となった、テンプレートを駆使したライブラリ群→現在では C++標準ライブラリの

import java.util.*;public class Sample62 {public static void main(String[] args) {Ring<Integer> a = new Ring<Integer>(0x0f, 10);for(int i = 0; i < 100; ++i)a.add(i+9);for(int v : a)System.out.println(v);}}class Ring<T> implements Iterable<T> {final int mask, size;
ArrayList<T> arr;
int count = 0;

Standard Template Library (STL)

コンテナクラス部分のことをそう呼ぶ。重要なアイデア:• テンプレートを使った効率のよいコンテナクラス群• 要素アクセスはコンテナクラスのメソッドではなくイテレータ経由 (その方が高速化しやすい)• イテレータに対して働くアルゴリズム群。find (線形探索)、sort (整列)、count(計数)、などなど。
• アルゴリズム群はテンプレート関数 (型パラメタつき関数) →高速。

□ 例:

普通の swap(C 版)

void swap(int *x, int *y) {int z = *x; *x = *y; *y = z;public Ring(int m, int s) {18

void operator()(T x) { res += x; cout<<res<<"\n"; }T result() const { return res; }}int a[100]; ... swap(&a[i], &a[j]) ...
};• 関数呼び出しのオーバヘッド、ポインタ経由のアクセス→遅い。しかも型ごとに用意する必要。

int main() {vector<int> a;for(int i = 0; i < 10; ++i)a.push_back(i);sum<int> s(0);s = for_each(a.begin(), a.end(), s);cout << s.result() << ’\n’;}□ 関数テンプレート版の swap(C++版)
template<typename T> inline void swap(T& x, T& y) {T z = x; x = y; y = z;}float a[100]; ... swap(a[i], a[j]) ...

• ループ周回を通じて保存されて欲しいものは関数オブジェクトのインスタンス変数にすればよい。→ ルー• 関数テンプレートの型パラメタは推定される (a[i]等が float だから T は float) →繁雑な指定が不要

プ構造と処理の分離• 「float z = a[i]; a[i] = a[j]; a[j] = z;」
がこの場所に埋められているのと同様→高速□ アルゴリズム関数を利用すれば制御構造をこちら側で書 6.8 テンプレートの特殊化
かなくてもよくなる。たとえば for each を使うとルー□ テンプレートで一般向けの実装のほかに「特殊ケース」
プが不要になる。
を用意したいことがある。
//sample63#include <algorithm>#include <vector>#include <iostream>using namespace std;

• たとえば bool(0/1) の ring だったら 1 ワードにシフトでデータを入れて行くとか…
• コンテナクラスの場合、「ポインタ用」はそれなりのポインタ用のものを使う方がよさそう… (「ポインtemplate<typename T> void f(T x) {cout << x << ’\n’;}

タ用」のクラスを 1 つ用意して残りはすべてそれを呼ぶ→コードを何回も展開しなくて済む→コード量の爆発を防ぐ上で重要なテクニック)

int main() {vector<int> a;for(int i = 0; i < 10; ++i)a.push_back(i);for_each(a.begin(), a.end(), &f<int>);}□ そのような「特殊ケース」を別途用意できる→特殊化
template <typename T> class ring {//一般}
template <typename T> class ring<T*> {// ポインタ用}
template <> class <bool> {// bool 用}• これで for each の中で「f(値)」が繰り返し呼ばれる→「ベクタの各要素を出力」ができる。ループは不要。

• しかし「合計を取る」だとどうするか?
□ 関数オブジェクト… operator() を持つようなオブジェ
クト。先の「f(値)」というのは関数でなく operator()の呼び出しでもよいので。
//sample33b#include <iostream>#include <vector>#include <algorithm>
using namespace std;
template<typename T> class sum {T res;
public:sum(T init) { res = init; }型パラメタに対する制約

6.9
□ 例:

maxbuf<T>というコンテナを作る。

• 次々に put(T x) で値を入れていく。
• T get() でその時点での最大値を取得できる。
//sample34#include <iostream>
template<typename T> class maxbuf {T max;
public:19

• 実際、極めて分かりづらいエラーメッセージに遭遇

maxbuf(T x) : max(x) { }void put(T x) { if(max < x) max = x; }T get() { return max; }};することがよくある• 本来だったら「この型パラメタにはこういう型しかあてはめてはいけませんよ」という指定ができた方

int main() {int a1[] = { 5, 9, 2, 7, 6 };maxbuf<int> m1(a1[0]);for(int i = 1; i < 5; ++i) m1.put(a1[i]);cout << "a1: " << m1.get() << ’\n’;
double a2[] = { 3.0, -2.7, 6e2, 4e1, 0.5 };maxbuf<double> m2(a2[0]);for(int i = 1; i < 5; ++i) m2.put(a2[i]);cout << "a2: " << m2.get() << ’\n’;}がいいのでは?

指定が繁雑になり自由度が減る。ともあれ、Java Generics
ではこちらの立場。
□ 上記の Java Generics 版。整数は Integer クラスを使う必要。
import java.util.*;public class Sample33 {public static void main(String[] args) {int[] a1 = { 5, 3, 8, 2, 4 };Maxbuf<Integer> m1 = new Maxbuf<Integer>(a1[0]);for(int i = 0; i < 5; ++i) m1.put(a1[i]);System.out.println(m1.get());
String[] a2 = { "this", "by", "foo", "z", "x" };Maxbuf<String> m2 = new Maxbuf<String>(a2[0]);for(int i = 0; i < 5; ++i) m2.put(a2[i]);System.out.println(m2.get());}}□ 型 T には演算子「<」が必要。これを満たさないと…
//sample34b#include <iostream>
template<typename T> class maxbuf {T max;
public:maxbuf(T x) : max(x) { }void put(T x) { if(max < x) max = x; }T get() { return max; }};class Maxbuf<T extends Comparable<T>> {T max;
public Maxbuf(T x) { max = x; }public void put(T x) {if(max.compareTo(x) < 0) max = x;}public T get() { return max; }}class point {double x, y;
public:point(double a, double b): x(a), y(b) { }};int main() {point a3[] = { point(0,0), point(1,1),
point(2,2), point(3,3), point(4,4) };maxbuf<point> m3(a3[0]);for(int i = 1; i < 5; ++i) m3.put(a3[i]);}□ Comparable<T>というのは x.compareTo(T y) というメソッドを持ち、このメソッドが x と y の大小関係に応
じて正/負/零を返す。
□ この方が確かにあらかじめチェックはできるが、窮屈かも□ 上記のをコンパイルすると…

…
□ あらかじめ Comparable<T>を implements しているク

In method ‘void maxbuf<point>::put(point)’:
22:
instantiated from here
7: no match for ‘point & < point &’ラスにしか使えない→自分がソースを持っていればいいが、そうでないと…

□ 確かに「<」がない、と言われる… 次のを入れればよくなるが。

• 実は C++版にも同様の問題がある。たとえば char*の maxbuf を取ろうとすると「アドレスが最大の」ものが取れてしまう…求めていることと違う。

bool operator<(point &p) {return x*x+y*y < p.x*p.x+p.y*p.y;}□ C++の場合は、単独関数テンプレートと特殊化を使えばOK。

• しかし「コンパイルしてみたら無かった」では実装の中に立ち入っていてエラーメッセージとしてあま //sample34c
り良くないのでは…

→ 賛否両論。否定派:#include <iostream>#include <cstring>using namespace std;20

class maxbuf {int max;
public:maxbuf() : max(0) { }void update(int v) { if(v>max) max=v; }int getValue() { return max; }};template<typename T> bool lt(T x, T y) {return x < y;}template<> bool lt(char* x, char* y) {return strcmp(x, y) < 0;}
template<typename T> class maxbuf {T max;
public:maxbuf(T x) : max(x) { }void put(T x) { if(lt(max, x)) max = x; }T get() { return max; }};□ 「update の回数を数える」
class count {int num;
public:count() : num(0) { }void update(int v) { ++num; update(v); }int getCount() { return num; }};int main() {int a1[] = { 5, 9, 2, 7, 6 };maxbuf<int> m1(a1[0]);for(int i = 1; i < 5; ++i) m1.put(a1[i]);cout << "a1: " << m1.get() << ’\n’;
char* a2[] = { "this", "by", "foo", "z", "x" };maxbuf<char*> m2(a2[0]);for(int i = 1; i < 5; ++i) m2.put(a2[i]);cout << "a2: " << m2.get() << ’\n’;}□ 「更新履歴を記録する」
class history {int record[1000], nrecs;
public:history() : nrecs(0) { }void update(int v) {record[nrecs++] = getValue(); update(v);}void showHistory() {for(int i = 0; i < nrecs; ++i)
cout << ’ ’ << record[i];cout << ’\n’;}};□ 特殊化の応用• 一般の T →「<」が使われる• char* → strcmp が使われる• その他特殊ケースはいくらでも対応可能 … C++の実用性に 1 日の長があるかも?

6.10□ これを次のようにして使いたい。
class MyClass :public balance, count, history {};テンプレートによる mixin クラス□ 前回やったように、mixin とは「他のクラスに混ぜるためのクラス」□ flavors ではこれでできる (どっちの update が呼ばれるかの規則があって OK)。しかし C++ではそもそも
getValue() や update() の宣言がないと型検査できな• それ自体単独でインスタンスを生成しないことから「抽象サブクラス」と呼ぶこともくてアウト。
• 前回の話では flavors などの多重継承を使って混ぜ□ ではどうするか…
ていた• しかし C++のような静的検査な言語では多重継承で• count や history で親クラスを指定すればもちろん動くようになる。

はうまく行かない□ たとえば、次のようなクラスを考える。
class balance {int value;
public:balance() : value(0) { }void update(int v) { value += v; }int getValue() { return value; }};• しかしさまざまなものにこれらの機能を組み込もうと思って作ったのに、決め打ちで親クラスを書いてしまうのでは 1 つにしか使えない (または繰り返し
コピーになる) のでよろしくな。

• ではどうすればいい?
□ 答: その親クラスをテンプレートパラメタで指定する!!

21

□ そのようなものを実現するには→ C++でもクラスの中にクラスが書けることを利用し、外側クラスをパラメタつきとする。

//sample65#include <iostream>class balance {int value;
public:balance() : value(0) { }void update(int v) { value += v; }int getValue() { return value; }};template<class T> class ThisLayer : public T {public:class First : public T::First { ... };class Second : public T::Second { ... };...
};class maxbuf {int max;
public:maxbuf() : max(0) { }void update(int v) { if(v>max) max=v; }int getValue() { return max; }};□ こうすれば、一群のクラスで Fist は First どうし、Second は Second どうし、…で縦に合成が起きる。
• ThisLayer の中にはこの層が実現する機能のためのデータやコードをまとめて入れることができる。

• このような構成を「mixin layers」と呼ぶ。

template<class T> class count : public T {int num;
public:count() : num(0) { }void update(int v) { ++num; T::update(v); }int getCount() { return num; }};• このような「構成法」の代替案→言語自体の拡張 (AOP
言語)

本節のまとめ

6.11□ 総称的プログラミング (型パラメタ、パラメタつき型)

template<class T> class history : public T {int record[1000], nrecs;
public:history() : nrecs(0) { }void update(int v) {record[nrecs++] = T::getValue(); T::update(v);}void showHistory() {for(int i = 0; i < nrecs; ++i)
cout << ’ ’ << record[i];cout << ’\n’;}};→ C++ テンプレート、Java Genrics で実現 (内容はかなり違っている)。主な用途:

コンテナクラス□ STL(C++) → イテレータによるアクセス、汎用アルゴ
リズム□ 型パラメタの制約 → 賛否分かれるところ。Java では
インタフェース/サブクラスによる制約指定可能 (だが不自由でもある) → C++に対する制約機構 (concepts)
は設計途上 → 後で少し紹介□ mixin クラス (C++) → 親クラスを拡張するための抽象

サブクラス
int main() {int a[] = { 5, 9, 2, 7, 6 };□ 全体として C++テンプレートは強力、しかし難しい。
history< count< balance > > c1;
count < history< maxbuf > > c2;
for(int i = 0; i < 5; ++i) {c1.update(a[i]); c2.update(a[i]);7 テンプレートメタプログラミング}
cout << "c1: " << c1.getCount(); c1.showHistory();cout << "c2: " << c2.getCount(); c2.showHistory(); 7.1 テンプレートメタプログラミングとは}□ メタプログラミング --- 「プログラム自身を参照/変□ mixin クラスを使うことで、1 つのクラスに盛り込まれ更するようなプログラミング」ている複数の側面を分離して記述し、使う時に組み立て □ 先にメタオブジェクトプロトコル (MOP) について紹介ることができる。

した□ しかし、実際には 1 つの「側面」が複数のクラスにまたがって存在していることが普通→このような「一群のクラス」を 1 つの「層」として、それを積み重ねてシステ• (復習) オブジェクト指向的に言えば → 「クラス」というオブジェクトが存在し、それに対するメソッドが呼べる。そのメソッドによって、クラスに関するムが構成されるというイメージ。

情報を取り出したり、クラスの内容を変更 (!)

22→

クラス定義を変更するということは、そのクラスに

enum { RET = 1 };属するオブジェクトの動作が変更されるということ

};

(!!)

int main() {std::cout << Fact<5>::RET << ’\n’;}• 実際には、コンパイルする言語では「動作を変える」のは難しい (実行中に動作を変更しようとしても、変更後の動作のコードをコンパイルできない…もうコ• (std::cout としているのは using namespace stdを使わないため)

ンパイル段階は終わっているから) → インタプリタ
系の言語 (ないし、コンパイラが実行系に入ってい• テンプレートから値を返すには enum 型で定数を定義るような言語) でのみ実用化すればよい→その定数を「型::名前」で参照可能□ コンパイル時 MOP --- 上記とは別のアプローチとして、
コンパイル時に動作するプログラム (メタプログラム)
を分けて記述• 数値が 0 かどうかの判定→テンプレートの特殊化で実装• この「120」はコンパイル時に計算が終わっている→速い (といってもこの場合はたいしたことはないが)• これなら、コンパイル時にメタプログラムが動き、プ

ログラムを加工してから、コンパイルに進めばよい。 □ 類似例:
そのかわり、メタプログラムはコンパイラと一緒に
動く必要• Open C++などの研究用言語があったが普及せず• C++のテンプレート機構を使えばコンパイル時に計算できるじゃん!

(誰が発見したのだろう?) → テ
ンプレートメタプログラミング□ テンプレートの実体化について整理すると…

• コンパイル時に「必要になったところで」行われる

組み合わせの数を「コンパイル時に」計算//sample71b#include <iostream>#include <ctime>
template<int n> class Fact {public:enum { RET = Fact<n-1>::RET * n };};template<> class Fact<0> {public:enum { RET = 1 };};ことになっている。

• パラメタには整数値も書けて、整数の計算はコンパ
イル時に行われる• 特殊化を応用すると「パラメタがこの値ならこちら」という分岐が可能□ →全体として、テンプレート機能を使って「コンパイル

int fact(int n) {return (n<=1) ? 1 : n*fact(n-1);}
int comb(int n, int k) {return fact(n)/(fact(k)*fact(n-k));}時の計算」ができる→「計算の結果によりプログラムを作る」ことができる→ テンプレートメタプログラミ
ング。

7.2
□ 例:

template<int n, int k> class Comb {enum { a = Fact<n>::RET,b = Fact<k>::RET * Fact<n-k>::RET };public:enum { RET = a / b };};int main() {std::cout << Comb<10,4>::RET << ’\n’;
std::cout << comb(10,4) << ’\n’;
int i, v1 = 0, v2 = 0;
clock_t t1 = clock();
for(i=0;i<1000000;++i) v1 += Comb<10,4>::RET;
clock_t t2 = clock();
for(i=0;i<1000000;++i) v2 += comb(10,4);
clock_t t3 = clock();
std::cout << double(t2-t1)/CLOCKS_PER_SEC << ’\n’;
std::cout << double(t3-t2)/CLOCKS_PER_SEC << ’\n’;}

基本的な例
階乗を「コンパイル時に」計算//sample71.cc#include <iostream>
template<int n> class Fact {public:enum { RET = Fact<n-1>::RET * n };};template<> class Fact<0> {public:• さっきのと同様だが計測してみる…
23

210
210
0.0234375 ←はるかに速い (アタリマエ)
1.27344

public:template<class Then, class Else> class Res {public:typedef Then RET;};};
class SelElse {7.3 テンプレートメタプログラミングの制御構
public:template<class Then, class Else> class Res {造
public:□ もっと一般的な「IF 文」を作ることもできる。
typedef Else RET;};//sample72};#include <iostream>
template<bool cond> class Select {public:template<bool cond, class Then, class Else>
typedef SelThen RET;
class IF {};public:template<> class Select<false> {typedef Then RET;
public:};typedef SelElse RET;
template<class Then, class Else>};class IF<false,Then,Else> {template<bool cond, class Then, class Else> class IF {public:typedef typename Select<cond>::RET which;
typedef Else RET;
public:};typedef typename which::template Res<Then,Else>::RET RET;
template<int m> class FactThen {};public:template<int m> class FactThen {enum { RET = m };public:};enum { RET = m };template<int n, int m> class Fact {};typedef typename IF< (n<=0),FactThen<m>,Fact<n-1,m*n> template<int>::RET result;n, int m> class Fact {public:typedef typename IF< (n<=0),FactThen<m>,Fact<n-1,m*n> >::RET
enum { RET = result::RET };public:};enum { RET = result::RET };};int main() {int main() {std::cout << Fact<5,1>::RET << ’\n’;
std::cout << Fact<5,1>::RET << ’\n’;}}• IF の Then 部、Else 部はまた別のクラス (=テンプレートのインスタンス) を渡すようになっている。条• 要するに 1 引数の Select で SelThen か SelElse か
どっちかのテンプレートを返す。Select は 1 引数だ

件は bool 値。

から特殊化しても部分特殊化にならない。

• 「> >」のところは間をあけないと「>>」(シフト)になってしまい構文エラーになるので注意 (すごく• 以下の部分特殊化を使うものも全部同様に書き直せ
るが略。

嫌!)

• ただし、コンパイラによっては「テンプレートの部分
特殊化」がサポートされていなくてこれでは通らな

7.4

大量コードの生成< false, □ 単一の値を計算するだけではあまり面白くないが、もっと長いコードを生成させることもできる。例: pow<3>(x)Then, Else > のように最初のパラメタだけ特殊化→ x*x*x。
することをいう)

いこともある (らしい)。(部分特殊化:□ 部分特殊化を使わないように書き直すこともできる (が面倒)。

//sample38#include <iostream>
template<int n> double pow(const double& x) {return pow<n-1>(x) * x;}
template<> double pow<1>(const double& x) {return x;//sample72b#include <iostream>class SelThen {24• 最適化を書けてインライン展開させないと無意味。

}

1.010291.010291.01029e+06
1.01029e+06
2.70312 ←倍くらい速い (思ったほどでもない?)
4.5625

int main() {double x = 2.0;
std::cout << pow<10>(x) << ’\n’;}• ただし、この方法で pow<100>(x) とかやらせようとしても、テンプレート再帰呼び出しの深さが深くな □ 普通に「i=0,1,2,... とループする」があった方が便利→作ってみた。
りすぎるといって止められてしまう。
□ とりあえず、2 のべき乗個の展開を、区間を半分ずつにして再帰呼び出しする方法で書いてみた。
//sample73b#include <iostream>#include <ctime>//sample74#include <iostream>using namespace std;
template<int n, int b, template<int i>class Stat>
class DO1 {public:template<class Env>
static void exec(Env& e) {DO1<n/2,b,Stat>::exec(e);DO1<n/2,b+n/2,Stat>::exec(e);}};template<int b, template<int i>class Stat>
class DO1<1,b,Stat> {public:template<class Env>
static void exec(Env& e) { Stat<b>::exec(e); }};template<int n, int b> class pow0 {public:static double calc(const double& x) {return pow0<n/2,b>::calc(x) *pow0<n/2,b+n/2>::calc(x);}};template<int b> class pow0<1,b> {public:static double calc(const double& x) {return x;}};

struct PowEnv { double r, x; };template<int i> class PowStat {public:static void exec(PowEnv &e) { e.r *= e.x; }};double pow1(int n, double x) {double r = 1.0;
for(int i = 0; i < n; ++i) r *= x;
return r;}

int main() {PowEnv e; e.r = 1.0; e.x = 1.00001;
DO1<1024,0,PowStat>::exec(e);cout << e.r << ’\n’;}

int main() {double x = 1.00001;
std::cout << pow0<1024,0>::calc(x) << ’\n’;
std::cout << pow1(1024, x) << ’\n’;
int i; double v1 = 0, v2 = 0;
• 今度は変数は「環境 (enviroment)」に格納し、exec
clock_t t1 = clock();
for(i=0;i<1000000;++i) v1 += pow0<1024,0>::calc(x);を呼ぶところで引数として渡す。結果も環境に格納。
clock_t t2 = clock();
for(i=0;i<1000000;++i) v2 += pow1(1024, x);□ もっと実用ぽく、「ベクトルの加算」をやってみた。
clock_t t3 = clock();
std::cout << v1 << ’\n’;//sample39b
std::cout << v2 << ’\n’;#include <iostream>
std::cout << double(t2-t1)/CLOCKS_PER_SEC << ’\n’;#include <ctime>
std::cout << double(t3-t2)/CLOCKS_PER_SEC << ’\n’;}
template<int n, int b, template<int i>class Stat>
class DO1 {public:• この場合、テンプレート側 (pow0) では本当に 1023
template<class Env>
個のかけ算命令が並ぶことになる→ループ制御が不static void exec(Env& e) {要、コンパイルはのろい
DO1<n/2,b,Stat>::exec(e);DO1<n/2,b+n/2,Stat>::exec(e);• コードも大きくなるので、キャッシュを考えると得か}
どうはか場合による。
};

25

};
template<int iv, int jr, int jv,
template<int i, int j>class Stat>
class DO2<1,jr,iv,jv,Stat> {public:template<class Env>
static void exec(Env& e) {DO2<1,jr/2,iv,jv,Stat>::exec(e);struct VecEnv {DO2<1,jr/2,iv,jv+jr/2,Stat>::exec(e);double r, a[1024], b[1024];}};};
template<int i> class VecStat {template<int iv, int jv,
public:static void exec(VecEnv &e) { e.r += e.a[i]*e.b[i]; } template<int i, int j>class Stat>
class DO2<1,1,iv,jv,Stat> {};public:template<class Env>
double iprod(double a[1024], double b[1024]) {static void exec(Env& e) { Stat<iv,jv>::exec(e); }double r = 0.0;};
for(int i = 0; i < 1024; ++i) r += a[i]*b[i];return r;
struct TestEnv {}};template<int i,int j> class TestStat {int main() {public:VecEnv e; e.r = 0.0; int i; double v;
static void exec(TestEnv &e) {for(i = 0; i < 1024; ++i) e.a[i] = e.b[i] = i;std::cout << i << ’,’ << j << ’\n’;
DO1<1024,0,VecStat>::exec(e);}std::cout << e.r << ’\n’;};
std::cout << iprod(e.a, e.b) << ’\n’;
clock_t t1 = clock();
for(i=0;i<1000000;++i) DO1<1024,0,VecStat>::exec(e); int main() {TestEnv e;clock_t t2 = clock();
DO2<16,16,0,0,TestStat>::exec(e);for(i=0;i<1000000;++i) v = iprod(e.a, e.b);}clock_t t3 = clock();
std::cout << double(t2-t1)/CLOCKS_PER_SEC << ’\n’;
std::cout << double(t3-t2)/CLOCKS_PER_SEC << ’\n’;}

template<int b, template<int i>class Stat>
class DO1<1,b,Stat> {public:template<class Env>
static void exec(Env& e) { Stat<b>::exec(e); }};7.5

本節のまとめ• しかしこうやって全部展開したものが速いとは限ら□ テンプレート展開機構を用いてプログラムを「組み立ないてる」
3.5739e+08
3.5739e+08
8.49219←あれ?
2.74219• テンプレート展開機構の動作や記述方法は C++言語の通常の動作やその記述方法とはまったく違っている (むしろ関数型に近い)• ループ最適化はコンパイラ業界では研究し尽くされ• コンパイル時に計算してしまうので、効率的に有利(な場合)ている→単純に展開するより速い• 実際には「数個ずつ展開しつつループで回る」(部分• 弱点:

ループ展開) がよいとされている。
□ もちろん、2 次元や 3 次元も必要なら作れる。

テンプレートのデバッグは大変□ テンプレートメタプログラミングの将来…

//sample39c#include <iostream>• C++ライブラリ実装者にとっては、効率を向上させる

template<int ir, int jr, int iv, int jv,
template<int i, int j>class Stat> class DO2 {public:template<class Env>
static void exec(Env& e) {DO2<ir/2,jr,iv,jv,Stat>::exec(e);DO2<ir/2,jr,iv+ir/2,jv,Stat>::exec(e);}• 普通のプログラマがこれを駆使するようになるのか手段として有力どうかは?

• Boost MPL (http://www.boost.org/libs/mpl/doc/)→ Boost (C++の標準ライブラリに追加提案するものを集めたライブラリ群) に含まれる、テンプレー

26

トメタプログラミング向けライブラリ→ 普通の人

使い方:もこれを書きましょうという方向?

8

テンプレートパラメタの仕様記述

8.1

はじめに

vector<int> vec(100);fill(vec.begin(), vec.end(), 0);
double a[100];fill(a, a+100, 0.0);• 型パラメタ Iter と T は「どのような条件」を満たす必要がある?

□ 以下の内容はまだ C++言語に入っているものではない…

□ 従来の「一般的な」考えとしては、各型についてそれが研究段階持つメソッド名とその引数、返値の型を指定する• 次の C++規格では「このようなもの」が入ることが期待されている (が公式には何とも解らないし、次• しかし、C++の場合はあらゆる演算子がオーバロード
でき自動型変換もあるので、これを全部指定するのの規格まで何年か掛かる)は非現実的 (大変すぎる)• 元ネタの論文は以下のもの• テンプレート中に「x = y + z;」と書いてあったと• Gabriel Dos Reis, Bjane Stroustroup,

して、この「+」も「=」も C++のもともとの演算な

Specifying C++ Concepts, Proceedings on
Principles of Programing Languages 2006のか「operator+」等なのかは解らない (テンプレー(POPL’06), pp.

トはどちらでも対応可能)

295-308, 2006.

• もっと別な方法がいいのでは…

• Douglas Gregor, Jaakko Jarvi, Jeremy
Siek, Concepts: Linguistic Support for
Generic Programming in C++, Proceegindsof Object Oriented Systems, Languages

8.3□ ある型 (ないし型の並び) が満たすべき条件を指定するもの。たとえば先の fill() に使うための簡単化した
concept の例を示す。

and Applications 2006 (OOPSLA’06), pp.
291-310, 2006.

8.2

concept Mutable_fwd(typename Iter, typename T) {Var<Iter> p;
Var<const T> v;
Iter p = q;bool b = (p != q);++p;*p = v;};

テンプレートパラメタの仕様記述問題□ テンプレートはパラメタが「ある条件を満たしている」
ことを前提に書かれている• 現在はそのことはソースプログラム中に表現されていない• 実際にテンプレート中で使うコードと同等のものを• 「条件を満たしていない」パラメタを与えた場合、コ書いてみせることで「こういうことができないとい

ンパイルエラーになるがその箇所/内容はテンプレー

けませんよ」と指定

トのソース内になる• p や v はそれぞれ Iter 型、const T 型の変数だとする。「Iter p;」としてしまうとデフォルトの引数な• テンプレートを「利用するだけの」プログラムには理解できないしコンストラクタで初期化可能であるという条件に• テンプレートを作る時のこの「条件」に関する間違なってしまう。これを避けるため「Var<...>」といいも作る時には解らない□ 例: テンプレート関数 fiill --- 指定範囲を指定値で埋める

concept 記述

う形を使う。
□ 上の concept が規定している条件は次のようになる

template<typename Iter, typename T>
void fill(Iter first, Iter last, const T& t) {for(Iter cur = first; cur != last; ++cur )*cur = t;}• Iter 型の値はコピー初期化できる• Iter 型の値は「!=」で比較でき結果は論理値に変換できる• Iter 型の値は「++」前置演算できる
27

• 久野の思っていること:• Iter 型を dereference した先に T の値を入れられ言語は人間が使うものだかる (そのとき T の値を取り出す変数は書き換えられら、人間にとって分かりやすく書きやすいことが一ることはない)番大切。その点で Java は平易だけれどコードが長々しすぎる。C++はコンパクトに書けるけれど裏側で何□ 上のような concept 記述を用いて、fill() を次のように書くが起きているか分からなさすぎる。
□ 結局、理想の言語というのはありませんね (当り前)。でtemplate<typename Iter, typename T>
where Mutable_fwd<Iter, T>
void fill(Iter first, Iter last, const T& t) {for(Iter cur = first; cur != last; ++cur )*cur = t;}

はあなたが作りますか?

(半分本気)

より進んだ学習に向けて

10• テンプレート定義時に、concept の中身を用いてテ □ プログラミング言語分野の研究について…
ンプレートのコードをチェックできる• 過去の EDP 参加者のアンケート中で「講座の後自分• テンプレート利用時に、concept の中身を用いてテンで勉強を進められるように参考書籍などを紹介してプレートパラメタが条件を満たしていることをチェッ
欲しい」というご意見がありましたので、お役に立クする (満たしていなければこれこれの concept を
つか分かりませんが一応。
満たしていないというエラーメッセージ)• プログラミング言語の設計とか機能とかはかなり深• 強すぎず、弱すぎない concept 条件を指定すること
くて狭い分野なので、あまり適当な本がありません
が大切(先端の研究は論文を読むしかない)。

8.4• そもそも本数冊読んで済むのだと我々研究者の仕事は何なのかということになるわけで…

本節のまとめ• C++と Java も入門書・解説書は山のようにあります
が、今回喋ったような細かい話がきちんと「議論さ□ concept --- テンプレートパラメタが満たす条件を規定するもの

れて」いる本はあまりないです。

• 「どのように使うか」のコードを書けばいいというアイデア →記述しやすく解りやすい (代替案もある
がこの方がよさそう)• とはいっても、何も無いではつまらないので、いく
つか参考となる本を挙げておきます。
□ C++• concept 条件はテンプレートを作る側と使う側の「契約事項」。これを明示することにより、作る側も使う側もやりやすくなる• ビョーン・ストラウストラップ著, 岩谷宏訳, C++の設計と進化, ソフトバンク, 2005.

• まだ C++の標準として入るまでには時間が掛かりそう--- C++言語の設計者ストラウストラップが C++ 言語の進化の• テンプレートパラメタをきちんとするというのは永過程やそこで出て来たさまざまな設計上の選択などを議論。

年の課題なので次の改訂で入って欲しい…

• ビョーン・ストラウストラップ著, 長尾高弘訳, プロ

9グラミング言語 C++ 第 3 版, アジソンウェスレイ,全体のまとめ1998. --- C++言語の詳細について書いてあって本講座のような「楽しみ」がある C++の解説書はやっぱ□ C++と Java の違い…

りストラウストラップの本かなという感じ。

• 誰でも答えられそうな質問だけれど、突き詰めるとすごーく奥深い• 設計思想の違い:

Java →動的、安全、のろい。C++→静的 (コンパイル時)、速い、プログラマの責任。

• アンドレイ・アレキサンドレスク著, 村上雅章訳,
Modern C++ Design − ジェネリック・プログラミ
ングおよびデザイン・パターンを利用するための究極のテンプレート活用術 − , ピアソン, 2001.

---

C++のテンプレートを駆使するマニアックな本という
ことで。

28

□ Java• ケン・アーノルド, ジェームズ・ゴスリン, デビッ
ド・ホームズ著, 柴田芳樹訳, プログラミング言語

Java 第 4 版, ピアソン, 2007.

--- これも Java

言語の設計が執筆陣に入っている本で、各種機能について詳細に説明。

• ブ レット・マ ク ラ フ リ ン, デ イ ビッド・フ ラ ナ
ガ ン 著, 菅 野 良 二 訳, Java 5.0 Tiger, オ ラ イ
リ ー, 2007.

--- JDK 1.5 で 新 し く 入った 機 能(Generics、アノテーション等) に絞って解説。

□ 以上、和書を挙げましたが、英語でも構わないという人
は… 本を読むのもいいですが、英語論文を読むのもいいです。学会としては ACM(Association for Computing

Machinely, 米国計算機学会) がメジャー。ACM の言語関係の代表的な国際会議:• ACM Principles of Programming Languages(POPL)• ACM Object-Oriented Systems, Languages andApplications (OOPSLA)• ACM Programming Languaged Design and
Implementation (PLDI)• ACM (http://www.acm.org/) から会議録が買えます。またはオンラインで読める選択もあります。
□ そして、プログラミング言語研究に興味があるという人はぜひ GSSM へ…

29