プログラミング・演習II

プログラミングII
第 7 回
オブジェクトの配列
New, delete
参照
田向
単項演算子 *
2項演算子: x op y ;
単項演算子:
op z ;
ポインター 宣言 での * とは違う.
変数宣言時では ポインター型の変
数である事を宣言している.
→ 代入演算の時に,コンパイラが
型チェックをするのに使用する.
Int* p;
単項演算子 * は ポインタ変数
の値から アドレスを取り出して,値
取り出しの処理(アドレス・レジスタ
にアドレスを書き込む)を行う.
x = *p;
単項演算子 &
単項演算子 * と同じように,アドレ
ス操作の
単項演算子 &
がある.
単項演算子 & は 変数,オブジェ
クトのアドレスを取り出す演算.
myclass * mp ;
myclass myObj ;
mp = &myObj ;
int x;
int * p , q;
p= q;
x=*p;
オブジェクトのアドレス
ポインタ変数
– 対象(オブジェクト,変数)の
アドレスを値として持つ変数
– オブジェクトの先頭アドレスを値として
持つ
– コンパイラはオブジェクト名から先頭ア
ドレスを取り出すことが出来る
s1
strtype * sp1;
sp1= &s1;
ベースアドレス
f
IE L D
N A M
E 1
f
IE L D
N E M
E 2
オブジェクトのアドレス渡し
ポインター変数で渡す
クラス名 * ポインター変数名;
で定義する.
– オブジェクトが存在している場所
を教える.
• オブジェクトの先頭のアドレス
• オブジェクトはコピーしない
– どんなに大きくても先頭アドレスだけが
コピーされる
– オブジェクトのアドレスは
単項演算子 & で取り出せる
• &で取り出した値はアドレスである
のでポインタ変数にしまう.
• アドレスにも型があるとエラーチェッ
クができる
Objectのアドレス渡し
#include <iostream>
using namespace std;
class samp { int i;
public:
samp(int n) { i = n; }
void set_i(int n) { i = n; }
int get_i() { return i; } };
void sqr_it(samp *o){
o->set_i(o->get_i() * o->get_i());
cout << "iのコピーの値は " << o->get_i();
cout << "\n"; }
int main(){
samp a(10);
sqr_it(&a);
cout << "今、main()は変更された: ";
cout << a.get_i(); return 0; }
オブジェクトの配列
#include <iostream>
using namespace std;
class samp {
int a;
public:
void set_a(int n) { a = n; }
int get_a() { return a; }
};
int main(){
samp ob[4];
//普通に宣言できる
int i;
for(i=0; i<4; i++) ob[i].set_a(i);
for(i=0; i<4; i++) cout << ob[i].get_a( );
cout << "\n";
return 0;
}
初期値の設定も同時に出来る
#include <iostream>
using namespace std;
class samp {
int a;
public:
samp(int n) { a = n; }
int get_a() { return a; }
};
int main(){
samp ob[4] = { -1, -2, -3, -4 };
int i;
for(i=0; i<4; i++)
cout << ob[i].get_a() << ' ';
cout << "\n";
return 0;
}
2次元配列も容易
#include <iostream>
using namespace std;
class samp {
int a;
public:
samp(int n) { a = n; }
int get_a() { return a; }
};
int main(){
samp ob[4][2] = {
1, 2, 3, 4,
5, 6, 7, 8
int i;
for(i=0; i<4; i++) {
cout << ob[i][0].get_a() << ' ';
cout << ob[i][1].get_a() << "\n";
}
cout << "\n";
return 0; }
};
コンストラクタを使った初期化
#include <iostream>
using namespace std;
class samp {
int a, b;
public:
samp(int n, int m) { a = n; b = m; }
int get_a() { return a; }
int get_b() { return b; }
};
int main(){
samp ob[4][2] = {
samp(1, 2), samp(3, 4),
samp(5, 6), samp(7, 8),
samp(9, 10), samp(11, 12),
samp(13, 14), samp(15, 16)
};
int i;
for(i=0; i<4; i++) {
cout << ob[i][0].get_a() << ' ';
"\n";
cout << ob[i][1].get_a() << ' ';
"\n"; }
cout << "\n"; return 0; }
cout << ob[i][0].get_b() <<
cout << ob[i][1].get_b() <<
オブジェクトのポインタ
#include <iostream>
using namespace std;
class samp {
int a, b;
public:
samp(int n, int m) { a = n; b = m; };
int get_a() { return a; };
int get_b() { return b; }; };
int main(){
samp ob[4] = {
samp(1, 2),
samp(5, 6),
samp(3, 4),
samp(7, 8) };
samp * p;
p = ob; // 配列の開始アドレスを取得する
int i;
p ->a = (*p).a
cout << p -> get_a( ) << ' ';
cout << p -> get_b( ) << "\n";
for(i=0; i<4; i++) {
p++;
//
// 次のオブジェクトに進む }
cout << "\n"; return 0; }
練習
次のクラスを使用して,2行5列の2
次元配列を作成し,配列内の各オ
ブジェクトに任意の初期値を設定し
なさい.次に,配列の内容をポイン
タを用いて表示しなさい.
class a_type {
double a, b;
public:
a_type(double x, double y){
a = x;
b = y;
}
void show(){
cout << a << ‘ ‘ << b << “\n”;
}
};
this pointer
#include <iostream>
#include <cstring>
using namespace std;
class inventory {
char item [20];
double cost;
int on_hand;
public:
inventory(char * i, double c, int o)
{
strcpy(item, i); cost = c;
on_hand = o; }
void show();
};
void inventory::show(){
cout <<
item;
cout << ": $" <<
cout << " 在庫: " <<
on_hand << "\n";
}
int main(){
inventory ob("レンチ", 4.95, 4);
ob.show();
return 0; }
cost;
this pointer 明示して記述
#include <iostream>
#include <cstring>
using namespace std;
class inventory {
char item[20]; double cost; int on_hand;
public:
inventory(char *i, double c, int o) {
strcpy(this -> item, i); // thisポインタを
this -> cost = c;
// 使用してメンバに
this -> on_hand = o;
// アクセスする
}
void show(); };
void inventory::show(){
cout << this ->item; // thisポインタを使用してメ
ンバにアクセスする
cout << ": $" << this ->cost;
cout <<" 在庫:"<< this ->on_hand<<"\n";}
int main(){
4);
inventory ob("レンチ", 4.95,
ob.show(); return 0; }
New ,delete 演算子
#include <iostream>
using namespace std;
int main(){
int *p;
p = new int; // 整数のメモリを確保・割り当てる
if(!p) {
cout << "メモリ割り当てエラー\n";
return 1; }
*p = 1000;
cout << "pが指している整数型は: " << *p
<< "\n";
delete p; // メモリを解放する
return 0;
}
mallocにかわる. heap 領域に領域を確保
する.
動的オブジェクトを割り当てる
#include <iostream>
using namespace std;
class samp {
int i, j;
public:
void set_ij(int a, int b) { i=a; j=b; }
int get_product() { return i*j; }
};
int main(){
samp *p;
p = new samp; // new で オブジェクトを割り当てる
if(!p) { cout << "メモリ割り当てエラー\n";
return 1; }
p->set_ij(4, 5);
cout << "積は: " << p->get_product() <<
"\n";
return 0; }
動的オブジェクトを割り当てる
#include <iostream>
using namespace std;
class samp {
int i, j;
public:
samp(int a, int b) { i=a; j=b; }
int get_product() { return i*j; }
};
int main(){
samp *p;
// オブジェクトを割り当てて初期化する
p = new samp(6, 5);
if(!p) { cout << "メモリ割り当てエラー\n";
return 1; }
cout<<"積は: "<< p->get_product()<<"\n";
delete p;
return 0; }
配列のnew, delete
#include <iostream>
using namespace std;
int main(){
int *p;
// 5つの整数用のメモリを割り当てる
p = new int [5];
// 割り当てが成功したことを常に確認する
if(!p) { cout << "メモリ割り当てエラー\n";
return 1; }
int i;
for(i=0; i<5; i++) p[i] = i;
for(i=0; i<5; i++) {
cout << "整数型p[" << i << "]は: ";
cout << p[i] << "\n";
}
delete [ ] p; // メモリを解放する
return 0; }
オブジェクト配列でのnew,delete
#include <iostream>
using namespace std;
class samp { int i, j;
public:
void set_ij(int a, int b) { i=a; j=b; }
~samp() { cout << "デストラクタ呼び出し\n"; }
int get_product() { return i*j; } };
int main(){
samp *p;
int i;
p=
new samp [10]; // オブジェクト配列を割り当てる
if(!p) { cout << "メモリ割り当てエラー\n"; return
1; }
for(i=0; i<10; i++) p[i].set_ij(i, i);
for(i=0; i<10; i++) {
cout << "積 [" << i << "] は: ";
cout << p[i].get_product() << "\n"; }
delete [] p;
return 0; }
参照(reference)&変数
変数の別名(alias)として動作する
用途
1. 関数の引数として参照を渡す.
2. 関数から参照を返す.
3. 独立した参照(非推奨)
効果
1. 関数間でオブジェクトを渡す際,
コピーが発生しないため,巨大
なオブジェクトを取り扱うプログ
ラムを高速処理可能
2. 左辺で関数が呼べる
3. コピーコンストラクタ,演算子の
オーバーロードで利用される.
ポインタ仮引数を使用する場合
#include <iostream>
using namespace std;
void f(int *n); // ポインタ仮引数を宣言する
int main(){
int i = 0;
f(&i); // i のアドレスを取り出す&演算
cout << "iの新しい値: " << i << '\n';
return 0;
}
void f(int *pn){
*pn = 100; // nが指す引数に100を格納する
}
*演算子: ポインタ変数 pn の値=オブ
ジェクトiのアドレスの場所を取り出す.
= 取り出したアドレスの処に100を置く.
参照仮引数を使用する場合
#include <iostream>
using namespace std;
void f(int &n); // 参照仮引数を宣言する
int main(){
int i = 0;
f(i);
cout << "iの新しい値: " << i << '\n';
return 0;
}
// f( )関数は参照仮引数 & を使用する
void f(int &n) { // int &n = i; と同じ意味
// 次の文では * が必要ない *n=100
// n は i の別名である
n
= 100; // f()関数を呼び出すのに使用し
た引数nに100を格納する
}
参照の例-I
#include <iostream>
using namespace std;
void swapargs(int &x, int &y);
int main(){ int i, j;
i = 10; j = 19;
cout << "i: " << i << ", ";
cout << "j: " << j << "\n";
swapargs(i, j);
cout << "交換後: ";
cout << "i: " << i << ", ";
cout << "j: " << j << "\n";
void swapargs(int &x, int &y){
int t;
t = x; x = y;
y = t; }
x , y はi, j の別名なので,
i, jの値が交換される.
return 0; }
参照の例-II
#include <iostream>
using namespace std;
class myclass { int who;
public:
myclass(int n) { who = n;
cout<<"コンストラクタ呼び出し"<<who<<
"\n"; }
~myclass() { cout << "デストラクタ呼び出し "
<< who << "\n"; }
int id( ) { return who; } };
void f(myclass &o){ // oを参照によって渡す
// .演算子を使用していることに注意
cout << "受け取り " << o.id() << "\n"; }
int main(){
myclass x(1);
f(x); //fでXはコピーされない.fを抜ける時に
//デストラクタ ~myclass は呼ばれない
return 0; }
参照を返す関数の効能-I
#include <iostream>
#include <cstdlib>
using namespace std;
class array {
int size; char *p;
public:
array(int num);
~array() { delete [] p; }
char &put(int i);
char get(int i);
};
array::array(int num){
p = new char [num];
size = num;
}
char &array::put(int i){// 配列に情報を格納する
return p[i]; // p[i]への参照を返す
}
参照を返す関数の効能-II
// Get something from the array.
char array::get(int i){
return p[i]; // 文字を返す
}
int main(){
array a(10);
//左辺で関数が呼べる.普通の関数では不可
// 戻り値がアドレスだから出来る.
a.put(3) = ‘X’; a.put(2) = ‘R’;
cout << a.get(3) << a.get(2);
cout << "\n";
return 0;
}
独立参照
#include <iostream>
using namespace std;
int main()
{
int x;
int &ref = x; // 独立参照を作成する
x = 10;
ref = 10;
// この2つの文の
// 機能は同等である
ref = 100;
// 100を2回出力する
cout << x << ' ' << ref << "\n";
return 0;
}
refはxの別名である.
使用さえる事は少ない.
練習
次のプロトタイプ宣言を利用して,
orderで指定された桁数までnumの
桁数を上げるmag()という関数を作
成せよ.
例えば,num = 4, order = 2 のとき,
関数 mag() が終了したとき
num = 400 になるようにせよ.
void mag(long &num, long order);
まとめ:オブジェクトは物
• 物objectの情報classは存在する.
抽象classから具体化classの流れ
(派生).
物には内部構造(メンバー)がある.
物objectの中には物objectsがある.
物の中では物が同じように相互に関
係している.
– Classという情報として存在している.
• 画像,音,物理的,数学的,情報的に存
在するものは物: オブジェクトである.
• Windows, AV関連プログラムは物を扱う.
• 処理(プログラム)も物としてやり取りする.
Applet (application + let(小さい))
• 回路も物,HDL,LSI回路,仮想回路
WindowsもCRT画面内の物
myWindow-1
myWindow-3
myWindow-2
progII > myWindows::windows(…){…}
Rootはwindow OSでCRT全体.
すべてのwindowは,window OSにつながっている.
Windos OSにメッセージを送って,それぞれの
window オブジェクトに対して処理を行う.
Turbo C++によるアプリ例
• フリーのIDEであるTurbo C++を
利用したアプリの作成例を紹介
する.
• Windows アプリの基本的部品
である,Windowやボタン,画像
格納領域等は全て開発環境内
に準備されているクラスからオ
ブジェクトを生成して利用する.
• 上記クラスの具体的な実装方
法は知る必要が無い.
• 上記クラスの利用方法,すなわ
ちメンバ変数やメンバ関数,継
承関係などはヘルプやWEBで
検索可能.