ぼんさい塾

ぼんさいノートと補遺に関する素材や注釈です.ミスが多いので初稿から1週間を経た重要な修正のみ最終更新日を残しています.

g++による演習(4)

2014-01-31 23:46:06 | 暮らし

progC.pdf
progC-s.pdf
progC-e.pdf
記事一覧

Tree を変更:2014-02-05

 
                                              実行結果

二分探索木のリストです.Tree は抽象クラスで,オブジェクトを作れません.

/* --- p4.cpp (半角 < を全角 < で置換)--- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
class Node{
public:
  int d; Node *l, *r;
  Node(void){d = 0; l = r = NULL;}
  Node(int d0){Node( ); d = d0;}
  void show(char *s, Node *p){
    char sl[8], sr[8];
    if(strlen(s) > 7){exit(1);}
    if(p == NULL) return;

    strcpy(sl, s); strcpy(sr, s);
    show(strcat(sl, "L"), p->l);
    printf("%s: %d\n", s, p->d);
    show(strcat(sr, "R"), p->r);
  }
};
class Tree{
public:
  Node *root, *tmp, *idle; int sz;
  Tree(void){root = tmp = idle = NULL; sz = 0;}
  Tree(Node *p, int n){
    root = tmp = NULL; idle = p; sz = n;
    for(int k = 0; k < n-1; k++){p[k].r = &p[k+1];}
    p[n-1].r = NULL;
  }
  void push(Node *p){p->r = idle; idle = p; sz++;}
  void pop(void){
    if(sz == 0){exit(1);}
    tmp = idle; idle = idle->r; sz--;
    tmp->l = tmp->r = NULL;
  }
  virtual void ins(int k) = 0; //純粋仮想関数 {
    //pop( ); tmp->d = k; tmp->r = root; root = tmp;
  //} //デバッグ用
  virtual void del(int k) = 0; //純粋仮想関数 {
    //if(root == NULL || root->d != k){return;}
    //tmp = root->r; push(root); root = tmp;
  //} //デバッグ用
  virtual void show(void){root->show("", root);}
};
class STree: public Tree{
public:
  STree(Node *p, int n): Tree(p, n){ }
  Node* search(int k);
  void ins(int);
  void del(int);
};
Node* STree::search(int k){
  Node *p=root;
  while(p != NULL && p->d != k){
    if(p->d < k){p = p->r;}else{p = p->l;}
  }
  if(p == NULL){printf("not ");}
  printf("found\n"); return p;
}
void STree::ins(int k){
  Node *p=root;
  pop( ); if(tmp == NULL) return;
  while(p != NULL && k != p->d){
    if(k < p->d){
      if(p->l != NULL){p = p->l;}else{p->l = tmp; break;}
    }else{
      if(p->r != NULL){p = p->r;}else{p->r = tmp; break;}
    }
  }
  tmp->d = k; if(p == NULL){root = tmp;}
} //既存なら変更なし
void STree::del(int k){ //削除
  Node *p=root, *p1=root, *p2, *p3;
  while(p != NULL && p->d != k){
    p1 = p; //if(p->d == k){break;}
    p = (k < p->d ? p->l: p->r);
  } //「□?□:□」は[#19%31B]
  p2 = p; if(p == NULL){return;}
  if(p->l == NULL || p->r == NULL){
    if(p->l == NULL){p = p->r;}else{p = p->l;}
  }else{
    p3 = p = p->r;
    while(p->l != NULL){ p3 = p;
      if(p->l != NULL){p = p->l;}
    }
    if(p != p3){p3->l = p->r;}
    if(p == p3){p2->r = p3->r;}
    p->l = p2->l; p->r = p2->r;
  }
  push(p2);
  if(p1 == p2){root = p;}
  else if(p1->l == p2){p1->l = p;}
  else{p1->r = p;}
} //不在なら変更なし
//
int main(void){
  Node n[8]; STree t(n, 8);
  t.ins(7); t.ins(3); t.ins(5); t.ins(9); t.ins(2);
  t.show( ); printf("\n"); t.del(3); t.show( );
  return 0;
}

参考資料([n]は本文でも引用, *.pptはブロック解除が面倒なので未調査):


g++による演習 (3)

2014-01-29 17:51:47 | 暮らし
progC.pdf
progC-s.pdf
progC-e.pdf
記事一覧

                                    実行結果

p32.cpp は強引な配列の初期化 (真似しない方がいい?)

/* --- p31.cpp (半角 < を全角 < で置換)--- */
#include <stdio.h>
#include <string.h>
//void tshow(char*, tnode*); //tnode() の前だからエラー
/*--------------------------------------
typedef struct tnode{
  int d; struct tnode *l, *r;
  void show(char *s, tnode *p){
    //::tshow(s, p); //エラー
  }
} tnode;
--------------------------------------*/
struct tnode{
public:
  int d; struct tnode *l, *r;
  void show(char *s, tnode *p);
};
void tshow(char *s, tnode *p){
  char sl[8], sr[8];
  if(p == NULL) return;
  strcpy(sl, s); strcpy(sr, s);
  tshow(strcat(sl, "L"), p->l);
  printf("%s: %d\n", s, p->d);
  tshow(strcat(sr, "R"), p->r);
}
void tnode::show(char *s, tnode *p){
    ::tshow(s, p); //tshow() の後だからOK
  }
//--------------------------------------
int main(void){
  tnode tn[16]={
    {5}, {3, (tn+4), tn}, {9},
    {7, (tn+1), (tn+2)}, {2}
  };
  tn[0].show("", tn+3);
  return 0;
}
/* --- p32.cpp (半角 < を全角 < で置換)--- */
#include <stdio.h>
#include <string.h>
class Node{public:
  int d; Node *l, *r;
  Node(void){d = 0; l = r = NULL;}
  void show(char *s, Node *p){
    char sl[8], sr[8];
    if(p == NULL) return;
    strcpy(sl, s); strcpy(sr, s);
    show(strcat(sl, "L"), p->l);
    printf("%s: %d\n", s, p->d);
    show(strcat(sr, "R"), p->r);
  }
};
typedef struct tnode{int d; Node *l, *r;} tnode;
int main(void){
  Node n[8];
  tnode tn[8]={
    {5}, {3, (n+4), n}, {9}, {7, (n+1), (n+2)}, {2}
  };
  Node *p=n;
  for(int k = 0; k < 8; k++){
    //n[k].d = tn[k].d; n[k].l = tn[k].l; n[k].r = tn[k].r;
    *p = *((Node *)(int)(tn+k)); p++; //一応OK
    //正統派は reinterpret_cast を使うそうです[3](2014-02-03)
  }

  n[0].show("", n+3);
  return 0;
}

参考資料([n]は本文でも引用, *.pptはブロック解除が面倒なので未調査):
[1] C++。クラス内に、引数付きコンストラクタを持つクラスの配列を生成する
  http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14115618084
[2] その13 引数付きコンストラクを持つクラスの配列を初期化する方法
  http://marupeke296.com/TIPS_No13_ClassArrayInitialize.html
[3]  C++編(言語解説) 第24章 C++独自のキャスト
http://www.geocities.jp/ky_webid/cpp/language/024.html
CSample* p = reinterpret_cast<CSample*>( num ); //int num;


g++による演習 (2)

2014-01-29 12:21:58 | 暮らし
progC.pdf
progC-s.pdf
progC-e.pdf
記事一覧

 
                                            オブジェクトへのポインタの代入

簡単な継承の例です.

/* --- p2.cpp (半角 < を全角 < で置換)--- */
#include <stdio.h>
typedef struct tnode{
  int d; struct tnode *l, *r;
  tnode(void){d = 0; l = r = NULL;}
  tnode(int d0){tnode( ); d = d0;}
  void add(int k){d += k;}
} tnode;
struct tnode1: tnode{
  char *s;
  tnode1(void): tnode(5){s = "xy";}
  tnode1(int d0, char *s0): tnode(d0){
    s = s0;
  }
};
int main( ){
  char s1[8]="abc";
  tnode t(1); tnode1 t1(2, s1);
  tnode1 *a1; a1 = new tnode1[4]; //tnode1(void) がないとエラー
//
  t.add(3); printf("%d\n", t.d);
  t1.add(4); printf("%d, %s\n", t1.d, t1.s);
  printf("%d, %s\n", a1[0].d, a1[0].s);
  a1[2] = t1; printf("%s\n", a1[2].s);
  delete[] a1;
  printf("%d, %s\n", a1[0].d, a1[0].s); //直後だから上書きされていない
//
  tnode *p=&t; tnode1 *p1=&t1;
  printf("p->d == %d\n", p->d);
  printf("p1->d == %d\n", p1->d);
  p = p1; printf("p->d == %d\n", p->d);
  //p1 = p; //当然エラー
  //p1 = (void)p; //これもエラー
  p1 = (tnode1*)(int)p; //エラーにならない!(ブログ:2014-01-28)
  p->d = 5; printf("p1->d == %d\n", p1->d);
//
  printf("OK?\n");
  return 0;
}

参考資料([n]は本文でも引用, *.pptはブロック解除が面倒なので未調査):
[1] メモリの二重解放回避テク - akihiko's tech note
  http://d.hatena.ne.jp/aki-yam/20081205/1228490763
メモリを解放した後 (delete p1;),ほかの用途でメモリを確保し (p2=new T;),たまたまそれが解放したメモリと同じアドレスに割り当てられてしまった場合 (p1==p2),最初のメモリを二重解放すると (delete p1;),新しく確保したメモリ (p2) が解放されてしまう.この結果,新しく確保したメモリにアクセスすると値が書き換えられていたり,
セグメンテーションフォルトが発生する場合がある.プログラマはまず,新しく確保したメモリ (p2) に問題があることを疑うが,実は最初のメモリ (p1) の解放に問題があるという,大変デバッグしにくい問題に直面するのだ.
[2] C/C++のちょっとしたこと
  http://www.geocities.jp/chiakifujimon/pghint/c.html
これは、C/C++の文字列の特性をしっかり把握していないときに起こしてしまう典型的なエラーです。
※ 上例は無関係


gccによる演習 (12)

2014-01-28 20:19:17 | 暮らし
progC.pdf
progC-s.pdf
progC-e.pdf
記事一覧

                                多態性の実装

progC-e.pdf  [#7: 参考] の「Cから見たC++」の部分を脱稿しました.(2014-01-28取り消し)

参考資料([n]は本文でも引用, *.pptはブロック解除が面倒なので未調査):
[1] 仮想関数テーブル - Wikipedia
  http://ja.wikipedia.org/wiki/%E4%BB%AE%E6%83%B3%E9%96%A2%E6%95%B0%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB
[2] 多重継承 - Wikipedia
  http://ja.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E7%B6%99%E6%89%BF
  継承 (プログラミング) - Wikipedia に転送される
[3] 評価戦略 - Wikipedia
  http://ja.wikipedia.org/wiki/%E8%A9%95%E4%BE%A1%E6%88%A6%E7%95%A5
 C++ではなくC、Java、JavaScriptなどの値呼びしかない言語には参照呼びは無く、「それらの言語において似たように見えるもの」を「参照呼び」と呼ぶのは間違いである。間違った説明をネットに掲載したり、さらには書籍にまでする者が後を絶たず、非常に広範に流布しているが間違いである。
[4] テンプレート (プログラミング) - Wikipedia
  http://ja.wikipedia.org/wiki/%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)
[5] C++入門
  http://wisdom.sakura.ne.jp/programming/cpp/
  この講座はBorland C++ Compiler 5.5で動作確認しています
  ./cpp3.html  からC++への変更点
  ./cpp11.html 参照
  ./cpp27.html 演算子のオーバーロード
  ./cpp33.html テンプレート関数
  ./cpp35.html 例外処理
  ./cpp38.html 名前空間
[6] C++編(言語解説) トップページ
  http://www.geocities.jp/ky_webid/cpp/language/
  新サイトにおいて、新規で記事を書き下ろしています。
  ./015.html 参照
  ./017.html 演算子のオーバーロード
  ./025.html 例外機構
  ./025.html テンプレート関数
  http://www.geocities.jp/ky_webid/ProgrammingPlacePlus/cpp/language/
  ./002.html C言語との差異
  ./003.html 名前空間
[7] C++入門
  http://www.asahi-net.or.jp/~yf8k-kbys/newcpp0.html
  基礎知識は仮定しません
[8] C++入門 - Biglobe
  http://www5c.biglobe.ne.jp/~ecb/cpp/cpp00.html
  C言語を一通り学んだ方が次の1歩を踏み出すためのページです。

補足:(1) 多態性の実装を見て「オブジェクト = データメンバ + 仮想関数表」であることが理解できます.
(2) オブジェクトに直接関係しない項目は説明を省略しました. 


gccによる演習 (11)

2014-01-28 20:18:15 | 暮らし
progC.pdf
progC-s.pdf
progC-e.pdf
記事一覧

                                  Cから見たC++

                                   アドレス変換 !!

progC-e.pdf に [#7: 参考資料] の前半を追加しました.(2014-01-28取り消し)

参考資料([n]は本文でも引用, *.pptはブロック解除が面倒なので未調査):
[1] オブジェクト指向プログラミング - Wikipedia
  http://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0
[2] C++ - ウィキペディア
  http://ja.wikipedia.org/wiki/C%2B%2B
[3] C++編 トップページ
  http://www.geocities.jp/ky_webid/ProgrammingPlacePlus/cpp/index.html
  第2章 C言語との差異
  http://www.geocities.jp/ky_webid/ProgrammingPlacePlus/cpp/language/002.html
[4] キーワード (C++) - Wikipedia
  http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89_(C%2B%2B)
[5] デストラクタ - Wikipedia
  http://ja.wikipedia.org/wiki/%E3%83%87%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF
[6] 第1回 「オブジェクト,クラス,インスタンスの関係」 - ITpro
  http://itpro.nikkeibp.co.jp/free/NIP/NIPCOLUMN/20021126/1/
[7] その13 引数付きコンストラクを持つクラスの配列を初期化する方法
  http://marupeke296.com/TIPS_No13_ClassArrayInitialize.html 
[8] C++で大きな配列定数を初期化する方法(Javaのstaticブロックに相当 ...
  http://d.hatena.ne.jp/shogicraft/20120217/1329497890
[9] C++入門
  http://wisdom.sakura.ne.jp/programming/cpp/index.html
  3. CからC++への変更点
  http://wisdom.sakura.ne.jp/programming/cpp/cpp3.html

補足:(1) 構造体+メンバ関数=オブジェクト.C++の「struct」はCとは少し異なり,正常に動作していた *.c を *.cpp にするとエラーになることがあります(メッセージで分かるので説明は省略)
(2) 初期のオブジェクト指向プログラミングの説明は「隠蔽,継承,多態性」から始まっていましたが隠蔽に関する説明はほとんど省略しています --- 多態性は後半で.
(3) 「class Cell: public tnode {char *s};」としたのでは「p = search(k);」で「p」の値を求めても「q = (Cell*)p; printf("%s\n", q->s);」がエラーになるので「Cell」の定義をいくつか試していたのですが,「q = (Cell*)(int)p;」ができます --- 素直な派生でOK.
(4) 動作確認用手抜きプログラム