プログラミング言語論
DESCRIPTION
プログラミング言語論. 第 12 回 オブジェクト指向. 情報工学科 篠埜 功. プログラムの分割. 大きなプログラムは、分割して開発するのがよい。 手続きは分割の最も基本的なものである。. Module( モジュール ) は、関連する変数、手続き、型を一つにまとめるものである。 手続きが処理するデータの型を一つのモジュール内に入れ、データ型の実装を隠す。. (参考) Opaque type. Modula-2 では、モジュールから型を export するとき、 type stack; のように、型名だけを export することができる。 - PowerPoint PPT PresentationTRANSCRIPT
プログラミング言語論
情報工学科 篠埜 功
第 12 回 オブジェクト指向
プログラムの分割
大きなプログラムは、分割して開発するのがよい。手続きは分割の最も基本的なものである。
Module( モジュール ) は、関連する変数、手続き、型を一つにまとめるものである。
手続きが処理するデータの型を一つのモジュール内に入れ、データ型の実装を隠す。
(参考) Opaque typeModula-2 では、モジュールから型を export するとき、 type stack;のように、型名だけを export することができる。これを opaque export といい、 export された型をopaque type という。Import した側では、 stack 型の変数を var s, t: stackのように宣言できる。Modula-2 は opaque 型について、代入、等しさのチェックをサポートする。ただし、 opaque 型はポインタに限られ、等しさの判定はポインタの等しさで判定される。
C++ におけるクラスC++ におけるクラスは、レコード (C では構造体と呼ばれる ) を一般化したもの。クラスを宣言した後はクラス名を型名として使用でき、その型の変数を宣言したり、オブジェクトを生成したりできる。
struct Stack { int top; char elements [101]; char pop(); void push (char); Stack(); };
(例) class Stack { public: int top; char elements [101]; char pop(); void push (char); Stack();};
(参考) Simula 67 のクラスを C に移植して設計されたのが C++ 。
C++ でのクラス宣言 struct X { <declarations> };は、 class X { public : <declarations> }と同じであり、 class X { <declarations> };は、 struct X { private : <declarations> };と同じである。
クラス宣言の例 class Stack { int top; int size; char *elements; public: Stack (int n) {size=n; elements = new char[size]; top=0;} ~Stack() { delete elements; } void push (char a) {top++; elements[top] = a; char pop() {top--; return elements[top+1];};
オブジェクトの生成、破棄C++ ではオブジェクトは new で生成し、 delete で破棄する。任意の型 T について、 new Tによって T 型のオブジェクトが生成され、それへのポインタが返される。 delete pによって、 p が指しているオブジェクトが破棄される。( 例 ) 前ページの例の、 elements = new char [size]によって、 char 型を要素とする長さ size の配列が生成される。 elements[0], elements[1], …, elements[size-1] によって配列の各要素が得られる。また、 delete elements によって配列オブジェクトが破棄される。
例 : 後ろからも要素を追加可能なリスト
class List { Cell * rear; public: void put (int); void push (int); int pop(); int empty() { return rear == rear->next; } List() { rear = new Cell (0); } ~List() { while(!empty()) pop(); } };
class Cell { int info; Cell * next; Cell (int i) { info = i; next = this; } Cell (int i, Cell *n) {info = i; next = n; } friend class List; };List 中の関数は Cell の privateなメンバーにアクセスできる。
例(続き)
void List::push (int x) { rear->next = new Cell (x, rear->next); } void List::put (int x) { rear->info = x; rear = rear->next = new Cell (0, rear->next); } int List::pop() { if (empty()) return 0; Cell * front = rear->next; rear->next = front->next; int x = front->info; delete front; return x;}
テンプレート(例) template <class T> class Stack { int top; int size; T * elements; public: Stack (int n) {size=n; elements = new T[size]; top=0;} ~Stack() { delete elements; } void push (T a) { top++; elements[top]=a; } T pop() { top--; return elements[top+1]; } };
Stack 型の変数を宣言したりオブジェクトを生成したりするとき、 Stack<int> s(99); のように型を <> 内に引数として与える。
C と C++C++ は 1983 年、 Bjarne Stroustrup によって設計、開発された。 C の拡張として設計されており、ほとんどの C 言語のプログラムは C++ のプログラムであり、意味も同じである。ただし、 C と C++ で意味が違うプログラムがある。
コメントは、 C では /* … */ だが、 C++ では // …(C99 では // もコメントとして使えるが )
(例) int f (int a, int b) { return a //* */ b ;}
return の右に書かれている式は、 C89では a/b, C++ およびC99 では a となる。
C と C++ (続き)
int x[99]; void f() { struct x {int a;}; sizeof (x); }
C++ では、構造体型を名前付きで宣言する構文で宣言した場合、その名前のみで構造体型を表せる。(例1) struct test {int a;} test x;のように書いてよい。 C では、 struct test x; と書く必要がある。 (C++ で struct test x; と書いてもよいが。 )
(例 2 ) sizeof(x) は、 C では配列x のサイズ、 C++ では構造体 x のサイズ。 C では、構造体 x のサイズはsizeof(struct x)と書かなければならない。
C と C++ (続き)C では、 sizeof(‘a’) は sizeof(int) と同じである。C++ では、 sizeof(‘a’) は sizeof(char) と同じである。
C では、列挙型のサイズは sizeof(int) である。C++ では、列挙型のサイズは、処理系依存である。
(列挙型の例) enum color {RED, BLUE, YELLOW};のように列挙型 color を宣言すると、 C ではsizeof(enum color) は sizeof(int) と同じ、 C++ では処理系依存。
オブジェクト指向オブジェクト指向はシミュレーションを記述することを意図して考え出された。シミュレーションの中の要素がオブジェクトである。
(例) Simula ( Simulation language, 1967 )• Ole-Johan Dahl, Kristen Nygaard が開発• 最初のオブジェクト指向言語 ( オブジェク
ト指向という言葉はまだなかったが、オブジェクト指向における種々の概念が含まれていた )
• ALGOL の拡張として設計された• 空港のシステムの記述が重要な例となっ
ていた
オブジェクト指向[ オブジェクトの例 ] 乗客、航空会社のカウンター、行列、チケット、…
(外側からの見た場合)オブジェクト間でメッセージをやりとりすることにより計算が進んでいく。
(内側から見た場合)メッセージを受け取ったら、それに対応する手続きを実行する。この手続きのことを、メソッド (method) あるいはメンバ関数 (member function) という。
クラスの階層構造Shape
Box Ellipse
Circle
Line Text
Box Ellipse Line TextCircle
Shape
継承 (inheritance)
• 子クラスは親クラスのメソッド、変数を継承する(親クラスのメソッド、変数が子クラスのメソッド、変数になる)。
• 子クラスでは、追加でメソッドや変数を定義できる。同じ名前の場合には上書き (override) される。
(注意) overload は override とは全く別の概念。Overload は、引数の数や型が違うメソッドに同じ名前をつけること。
C++ の例C++ では、継承は以下のように記述する。
class Box : public Shape { … }
すべてのメンバーをvisibility を保って継承する。
C++ では親クラスを基底クラス (base class) 、子クラスを派生クラス (derived class) という。
class Box : private Shape { … }
継承されたメンバーは default で privateメンバーになる。
仮想関数 (virtual function)
class B { public: virtual char f () { return ‘B’; } char g() { return ‘B’; } char testF() {return f(); } char testG() { return g(); } };
class D : public B { public: char f () { return ‘D’; } char g() { return ‘D’; } }; #include <iostream> int main (void) { D d; std::cout << d.testF() << d.testG() << "\n"; return 0; }
d.testF() は’ D’ を返し、d.testG() は’ B’ を返す。
メソッド宣言に virtual というキーワードをつけると、(コンパイル時でなく)実行時にメソッドが選択される。
補足メソッドは、( C++ コンパイラ内で)引数を1つ追加した関数へコンパイルすればよい。
char testF() { return f(); }
char testF (B * this) { return this->f(); }
d.testF() を testF(d) にコンパイルすれば、(クラス B において f は virtual なので) testF の本体で d に対してメッセージ fが送られることになり、’ D’ が返される。
C++ の特徴• C との backward compatibility をできる限り保ちつつオブジェクト指向をサポートするように設計された。
• オブジェクトを C の構造体の拡張とした。つまり、オブジェクトは関数の activation recordや local block 内に allocate され得る。(もちろんヒープにも allocate できるが。)
• オブジェクト指向ではないプログラミング(命令型のプログラミング)もできる。(プログラミングのスタイルをプログラマに強要しない。)
• Multiple inheritance をサポートする。(授業範囲外とする)
オブジェクト指向言語のまとめ
• Dynamic lookup (メッセージを受け取ったときに実行されるメソッドは実行時に決まる)
• Abstraction ( public 関数がインターフェースとなり、データや実装は他の部分から見えない)
• Subtyping (派生クラス型の式は基底クラス型の式と置き換えてもよい( public で継承している場合))
• Inheritance (継承。コード量が削減され、コードを修正しやすくなる。)
オブジェクト指向言語 (object-oriented language)は、オブジェクトを持ち、以下の4つの特徴を持つ。
注意Subtyping と inheritance は異なる概念である。(例)Queue --- first-in, first-outStack --- last-in, first-outDequeue --- 両端から出し入れ可能な queue
Dequeue の派生クラスとして Queue クラスとStack クラスを実装することができる (private な継承を使って必要なメソッドのみ public にすれば ) 。しかし、 Queue, Stack は Dequeue の subtype ではない。
(参考) Data invariant• 制御がオブジェクト内にないときに常に成り立
つ性質• (例) bounded buffer (長さ制限付き queue )– put(x), get() の2つのメソッドからなる。– 配列に要素を格納し、 front と rear で範囲を示す。
配列の最後の要素の次は最初の要素とする。
オブジェクトは、 data invariant を考慮しつつ設計する
バッファーは front と rear が等しいとき空 Rear の次の要素が front のときバッファーは一杯 Front と rear の間に、入力された順に要素が並んで
いる。