記事一覧(Cの文法(1)~(17),他) まず覚えるCの文法(progC.pdf) P2.h: 段級位の追加 //------------------------------------- #include "P1.h" typedef struct{ int id, dan; } member2; class group2 : public group{ private: member2 *top2; int dan; public: group2(member*, int, member2*); void set_dan(int, int); void show(int); void add(int, char*, char*, int); void del(int); }; group2::group2( member *p, int m, member2 *p2 ):group(p, m){ top2=p2; } void group2::set_dan(int i, int d){ member *p=addr(i); top2[k].id=i; top2[k].dan=d; } void group2::show(int i){ group::show(i); if(top[k].id==top2[k].id){ printf("%d, %d\n", top[k].id, top2[k].dan); }else{printf("?\n");} } void group2::add( int i, char *n, char *m, int d ){ group::add(i, n, m); set_dan(i, d); } void group2::del(int i){ member *p=addr(0); member2 *q=&top2[k-1]; group::del(i); p=addr(q->id); if(p->id==q->id){ top2[k].id=q->id; top2[k].dan=q->dan; q->id=0; } } //------------------------------------- P2.cpp: 使用例 //------------------------------------- #include "P2.h" member igo[10]={ {1, "Aoki", "aoki@xxx.ne.jp"}, {2, "Itoh", "taro@yyy.jp"}, }; member2 igo2[10]={{1, 2}, {2, -1}}; int main(void){ member *p=&igo[0], *q; member2 *p2=&igo2[0]; group2 g_igo2(p, 10, p2); g_igo2.add(5, "Tanaka", "foo@zzz.ac.jp", 0); for(q=p; q->id!=0; q++){ g_igo2.show(q->id); } g_igo2.set_dan(5, 3); g_igo2.del(2); for(q=p; q->id!=0; q++){ g_igo2.show(q->id); } return 0; } //------------------------------------- |
================================================= 2. 派生クラス 囲碁,将棋に限らず柔道,剣道,書道,珠算等にも段 級位があります.2段を 2,3級を -3,不明を 0 と した段級位のデータを扱えるように, group を拡張し た派生クラス group2 の例を P2.h に示します. (1)「class group2 : public group」は group から 派生させることを意味します.基本クラスのデー タメンバやメンバ関数をそのまま使うときは何も 書く必要はありません. (2) 追加したいデータメンバや変更あるいは追加した いメンバ関数は P2.h のように明示します. 基本 クラスに不要なものがあっても削除はできません. (3) 派生クラスの定義は基本クラスには影響しません. ・簡単のため P1A.h でなく P1.h を用いました. ・group2 のコンストラクタの処理に追加する処理のみ を P2.h のように書きます. ・group2::show() の処理で group の show() を利用 したいときは「group::」を付けて呼び出します. ・set_dan() は段級位設定のために追加した関数です. ・show() は group::show() を使っているので途中で 改行されますが,これくらいは我慢しましょう. 細かい処理はともかく 基本クラスを利用すると派生ク ラスのプログラミングが非常に楽になります. *********** P2A.cpp: おまけ(無視してよい) //------------------------------------- #include "P2.h" member igo[10]={{1, "A", "x"}, {2, "B", "y"}}; member2 igo2[10]={{1, 1}, {2, -1}}; member tennis[20]={{3, "C", "z"}}; int main(void){ group *p1=new group(&tennis[0], 20), *p; group2 *p2=new group2(&igo[0], 10, &igo2[0]); member *q; char c; printf("club="); scanf("%c", &c); switch(c){ case 'i': p=p2; q=&igo[0]; break; case 't': p=p1; q=&tennis[0]; break; } for(/* q=p->top_is() */; q->id!=0; q++){ p->show(q->id); } return 0; } //------------------------------------- 多態性の説明です. P2A.cpp の p は group,group2 のいずれのオブジェクトを指すかは実行時に決まりま す.この結果,p->show() が group,group2 のいずれ のものであるかもコンパイル時には分かりません(late binding).このプログラムを実行して,キー入力によっ て表示形式が異なることを確認してください. ・group2 *p; のときは p=p1; とはできません.情報 不足で処理できないことが起こりうるからです. ・P2h では top_is() がないので q を直接設定しまし たが q=p->top_is(); が望ましい. ・どうして実現するか興味のある人は,まず[0-2]の仮 想関数テーブルの説明を見てください.[2-1] 第9章 オーバーライド (含. 多態性) [2-2] 第32章 privateやprotectedな継承 |
記事一覧(Cの文法(1)~(17),他) まず覚えるCの文法(progC.pdf) P1A-1: group クラスの変更例 //------------------------------------- class group{ protected: member *top; int max, k; member* addr(int); public: group(member*, int); member* top_is(void){return top;}; int max_is(void){return max;}; virtual void show(void); virtual void show(int); virtual void add(int, char*, char*); virtual void del(int); virtual void load(char*); virtual void save(char*); }; //------------------------------------- P1A-2: 追加関数 //------------------------------------- void group::show(void){ member *p=top; for(k=0; k<max; k++,p++){ if(p->id==0){break;} printf("%d, %s, %s\n", p->id, p->name, p->mail); } } void group::load(char *s){ FILE *fp=fopen(s, "r"); member *p=top; int i; char n[10], m[30]; if(fp==NULL){printf("-\n"); return;} for(k=0; k<max; k++,p++){ fscanf(fp, "%d %s %s\n", &i, n, m); p->id=i; strcpy(p->name, n); strcpy(p->mail, m); if(p->id==0){break;} } fclose(fp); } void group::save(char *s){ FILE *fp=fopen(s, "w"); member *p=top; if(fp==NULL){printf("-\n"); return;} for(k=0; k<max; k++,p++){ fprintf(fp, "%d\t%s\t%s\n", p->id, p->name, p->mail); if(p->id==0){break;} } fclose(fp); } //------------------------------------- |
P1.h の group クラスの変更案を P1A-1 に示します. 変更理由は (1) top と max を見えるようにする. (2) 全体を表示する show() を追加. (3) ファイル入出力用の関数を追加. で,追加した関数の構成例を P1A-2 に示しています. ・top や max の値は見るだけで変更できません. ・show(void) と show(int) は引数が違うので同じ名 前を使えます(printf や scanf は例外).関数の多 重定義については別途説明します. P1.h に変更を施したヘッダファイル P1A.h を作り, P1A.txt: 入力ファイル //------------------------------------- 1 Aoki aoki@xxx.ne.jp 2 Itoh taro@yyy.jp 5 Tanaka foo@zzz.ac.jp 0 //------------------------------------- を準備して, P1A.cpp: ファイル入出力 //------------------------------------- #include "P1A.h" int main(void){ member *p=new member[10]; group igo(&p[0], 10); igo.load("P1A.txt"); igo.show(); igo.del(2); igo.save("P1B.txt"); //delete[] p; return 0; } //------------------------------------- をコンパイルして実行し,出力ファイル P1B.txt の内 容を確認してください. ・「member *p; p=new member[10];」によって member の配列が動的に作成され,その先頭番地が p に設定 されます. ・new で作成した配列の削除は delete[] で行います. 「int *p=new int;」のときは「delete p;」です. ・P1A.cpp では 終了直前に領域を削除しても無意味な のでコメントにしています.[1-2] New演算子 - Wikipedia
|
記事一覧(Cの文法(1)~(17),他) まず覚えるCの文法(progC.pdf) P1.h: 基本クラス化 //------------------------------------- #include <stdio.h> #include <string.h> typedef struct{ int id; char name[10]; char mail[30]; } member; class group{ protected: member *top; int max, k; member* addr(int); public: group(member*, int); virtual void show(int); virtual void add(int, char*, char*); virtual void del(int); }; group::group(member *p, int m){ top=p; max=m; } member* group::addr(int i){ member *p=top; for(k=0; k<max; k++,p++){ if(p->id==i || p->id==0){break;} } if(k<10){return p;} else{return NULL;} } void group::show(int i){ member *p=addr(i); if(p->id==i){ printf("%d, %s, %s\n", p->id, p->name, p->mail); }else{printf("?\n");} } void group::add( int i, char *n, char *m ){ member *p=addr(i); if(p!=NULL && p->id==0){ p->id=i; strcpy(p->name, n); strcpy(p->mail, m); }else{printf("-\n");} } void group::del(int i){ member *p=addr(i), *q; if(p!=NULL && p->id==i){ q=addr(0); q--; if(q!=p && q->id!=0){ p->id=q->id; strcpy(p->name, q->name); strcpy(p->mail, q->mail); }else{q=p;} q->id=0; q->name[0]=q->mail[0]='\0'; }else{printf("?\n");} } //------------------------------------- |
================================================= 1. 基本クラス P0 のプログラムを基本クラスを使って書き換えた例を 示します.P1.h と P1.cpp はテキストファイルの名前 です.P1.cpp の先頭行「#include "P0.h"」は プリプ ロセッサによって P1.h の内容に置換されます(#66). P1.cpp: P0 のプログラムの書き換え (10/9修正) //------------------------------------- #include "P1.h" member igo[10]={ {1, "Aoki", "aoki@xxx.ne.jp"}, {2, "Itoh", "taro@yyy.jp"} }; int main(void){ member *p=&igo[0], *q; group g_igo(p, 10); g_igo.add(5, "Tanaka", "foo@zzz.ac.jp"); for(q=p; q->id!=0; q++){ g_igo.show(q->id); } g_igo.del(2); for(q=p; q->id!=0; q++){ g_igo.show(q->id); } return 0; } //------------------------------------- ・P1.cpp と同じフォルダに P1.h があれば「#include "P0.h"」と書きます. ・main() をいろいろ変えて実行したいときは,ファイ ルを分割しておくと便利です. P0 の構造体 group を基本クラス group に変更すると きの方針を以下に示します. (1) top, max, addr() は派生クラスでも使えるように protected にしておく.また,addr() で使う k は 派生クラスでの利用価値があるので addr() の外に 置き,protected にする. (2) 関数 show(), add(), del() は派生クラスで機能を 拡張することに対処するため「virtual」を付けて仮 想関数にしておく[0-2]. ・C++等では「見せる必要のないものは見せない」こと を方針にしています.アクセス指定子には private, protected, public があり,それぞれ自クラス内でしか参照できないもの, 自クラスと派生クラス内でしか参照できないもの,ど こからでも参照できるものを規定します. ・各クラスにはクラスと同じ名前を持つ関数(コンスト ラクタ)が存在して,インスタンスの初期化を行いま す.変数の宣言「int k=0;」に対応させると,int が クラス,k がインスタンス,「k=0;」がコンストラク タの処理に相当します. ・コンストラクタに対応するデストラクタは気にしなく て結構です.new で生成したものは delete が処理し てくれます[1-1].[1-1] デストラクタ - Wikipedia |
記事一覧(Cの文法(1)~(17),他) まず覚えるCの文法(progC.pdf) P0: Cの構造体と専用の関数 //------------------------------------- #include <stdio.h> #include <string.h> typedef struct{ int id; char name[10]; char mail[30]; } member; member igo[10]={ {1, "Aoki", "aoki@xxx.ne.jp"}, {2, "Itoh", "taro@yyy.jp"} }; //------------------------------------- typedef struct{ member *top; int max; } group; group g_igo={&igo[0], 10}; member *addr(group *g, int i){ member *p=g->top; int k; for(k=0; k<g->max; k++,p++){ if(p->id==i || p->id==0){break;} } if(k<g->max){return p;} else{return NULL;} } void show(group *g, int i){ member *p=addr(g, i); if(p->id==i){ printf("%d, %s, %s\n", p->id, p->name, p->mail); }else{printf("?\n");} } void add(group *g, int i, char *n, char *m){ member *p=addr(g, i); if(p!=NULL && p->id==0){ p->id=i; strcpy(p->name, n); strcpy(p->mail, m); }else{printf("-\n");} } void del(group *g, int i){ member *p=addr(g, i), *q; if(p!=NULL && p->id==i){ q=addr(g, 0); q--; if(q!=p && q->id!=0){ p->id=q->id; strcpy(p->name, q->name); strcpy(p->mail, q->mail); }else{q=p;} q->id=0; q->name[0]=q->mail[0]='\0'; }else{printf("?\n");} } //------------------------------------- int main(void){ member *p; add(&g_igo, 5, "Tanaka", "foo@zzz.ac.jp"); for(p=g_igo.top; p->id!=0; p++){ show(&g_igo, p->id); } del(&g_igo, 2); for(p=g_igo.top; p->id!=0; p++){ show(&g_igo, p->id); } return 0; } //------------------------------------- |
0. はじめに
C言語の知識を前提としてC++の概要を
1.基本クラス
2.派生クラス
3.多重定義
4.その他(10/15変更)
の順に説明します.したがって
#include <iostream.h>
int main(void){
cout << "Hello, world";
return 0;
}
の「iostream.h」「cout」等の説明は最後になります.
Cの代わりにC++を使う最大の利点は,基本クラスで
作られたプログラムを変更することなく,派生クラス
で機能を拡張できることです.
簡単な例として「Cの文法(16)」で宣言した構造体
group を基本クラスに変更して,段位のデータ等を付
加した派生クラスを作ってみましょう.
準備として「Cの文法」で考えた P0 のプログラムを
復習しておいてください.なお,Cでも
member *addr(group *g, int i);
void show(group *g, int i);
void add(group *g, int i, char *n, char *m);
void del(group *g, int i);
あるいは
member *addr(group*, int);
void show(group*, int);
void add(group*, int, char*, char*);
void del(group*, int);
のような宣言文(プロトタイプ宣言)があります.C
のコンパイラは関数の定義の前に呼び出しが書かれて
いると int 型の関数として処理してしまいますが,
冒頭に上記の宣言文をおくと,関数の定義を main()
の後で書いても正しくコンパイルされます.また,大
きなプログラムを複数のファイルに分割してコンパイ
ルするときには,このような表現は必須です(先頭に
「extrn」を付けます)(@46).
クラスの宣言に類似の表現を使うので,ついでに覚
えておきましょう.
参考:オブジェクト指向プログラミングの特徴や歴史
は[0-1]で説明されていますが,とりあえずオブジェク
トとはデータメンバだけで構成される構造体にメンバ
関数を付加したものと考えればいいでしょう.派生ク
ラスではデータメンバの追加やメンバ関数の変更・追
加が行われます.メンバ関数の扱いに興味のある人は
[0-2]を勉強してください.
[0-1] オブジェクト指向プログラミング - Wikipedia[0-2] 仮想関数テーブル - Wikipedia [0-3] 柴田望洋,プログラミング講義C++ [0-4] 講義資料(圧縮ファイル) |
記事一覧 「まず覚えるCの文法」 (校正中) P64: ファイル入出力 //------------------------------------- #include <stdio.h> #include <math.h> int main(void){ FILE *fp; int i, k; double x[10]; fp=fopen("log10.txt", "w"); if(fp==NULL){return 1;} for(i=1; i<10; i++){ printf("%d, %f\n", i, log10(i)); fprintf(fp, "%d, %f\n", i, log10(i)); } fclose(fp); printf("quit="); scanf("%d", &i); if(i!=0){return 0;} fp=fopen("log10.txt", "r"); if(fp==NULL){return 2;} for(i=1; i<10; i++){ fscanf(fp, "%d, %lf\n", &k, x+i); printf("%d, %f\n", k, x[i]); } fclose(fp); return 0; } //------------------------------------- P65: 強制終了 //------------------------------------- #include <stdio.h> double q(double x, double y){ if(y==0){ printf("div by 0\n"); exit(1); } return x/y; } int main(void){ double a, b; printf("a="); scanf("%lf", &a); printf("b="); scanf("%lf", &b); printf("a/b==%f\n", q(a, b)); return 0; } //------------------------------------- |
計算結果を画面に表示する代わりにテキストファイルに 出力しておくと,それをプリンタで印刷できます.また キーボードの代わりに予め作っておいたテキストファイ ルからデータを入力することもできます.具体的な方法 を常用対数表の作成と入出力を行う P64 のプログラムで 説明します. (1) (#64)の数学関数を使うには「#include <ath.h>」が 必要です. (2)「FILE *fp; fp=fopen("log10.txt", "w");」はファ イル log10.txt に出力準備をシステムに要求します. 準備に失敗すると fp の値は NULL になります. (3)「fprintf(fp, "%d, %f\n", i, log10(i));」の意味 は「printf("%d, %f\n", i, log10(i));」と同様です. (4) 処理が終了すれば「fclose(fp);」でファイルを解放 します. (5)「fp=fopen("log10.txt", "r");」はファイル log10. txt からの入力の準備をシステムに要求します. (6)「fscanf(fp, "%d, %lf\n", &k, x+i);」の意味は 「scanf("%d, %lf\n", &k, x+i);」と同様です. (7) 処理が終了すれば「fclose(fp);」でファイルを解放 します. ・fopen() の引数 "w" は,ファイルが存在しなければ作 成して出力し,存在すれば上書きします.ファイルの 末尾に追加するときは "a" を用います. ・[#62]の getc(), fgets(), fread(), [#63]の putc(), fputs(), fwrite() については各自調べてください. 実行中のプログラムを強制的に終了させるときは exit() を実行します.引数は任意の整数値で結構です.(習慣的 には 0 は正常終了) ・[#65]の rand(), malloc(), difftime() の機能や使用 例は各自調べてください. 問は省略します. =================================================--=== Cの文法(0): ぼんさいノート「まず覚えるCの文法」はオブジェクト指向 プログラミングの説明に必要な(オブジェクト以前の)予備 知識を与えるために作成した本文 12 頁の資料です. ぼんさいノート progJ.pdf は未着手です.とりあえず構造体 の説明は済んだので,「Cから見たC++」を書きながら内容を 考えます. |