プログラミング・演習II

プログラミングII
第 5 回
関数へのObject渡し
値,ポインター
田向
前回までの復習
クラスとオブジェクト
• データ構造を設計して決めるのが
class ABC {…};
• このデータ構造でメモリ領域を確保
して,オブジェクトとして宣言する.
ABC obj1;
ABC obj2;
• オブジェクトに対して操作する.
– 初期化,
– 入出力など
• クラスには,メンバー変数,メンバー
関数が定義できる.
– focus で見える,見えない
•
public, private など
C++プログラム作成・実行
• クラスをヘッダー・ファイル
xxxxx.H
に作る.
• クラスにはメンバー変数,メンバー
関数の宣言を書く.
• 実装コードを xxxxx.cppファイル
に書く.
– Class_name::func(…){…};
• main{・・・}でオブジェクトを生成し
て,メンバー関数を呼んで,メン
バー変数の読み書きを行う.
• メンバー変数の中でも,オブジェクト
を生成することが出来る.
コンストラクタ,デストラクタ
• メモリに動作領域を確保するのがコ
ンストラクタ
– 以前のコンパイラーは自動で暗
黙裡にやっていた
– ユーザーが明確に書かなければ
ならない.
• OS(システム)管理のメモリを要求
する.Cではmalloc()がしていた.
• 面倒だか,ダンプ記述(cout等)を
すればデバッグが容易になる
• 不要になったメモリを開放するのが
デストラクタ
• 要するに オブジェクトは生成され,
使用され,破棄される.
– なれるのは大変だが,大事
関数,メンバー関数
• Cでは関数の引数では値渡し.
– 引数の値を取り出して関数に
送っている.
– ポインタ変数に対して 変数のア
ドレスを値として渡している.
– 渡された値をアドレスと解釈して
メモリへの読み書きをしている.
– C++でも同じで値渡しである.
• 関数の引数がオブジェクトの場合に
はどうする?
– オブジェクトの値とは,一つでな
い.
– メンバー変数以外にメンバー関
数もある.メンバー関数の値は何
か?考えなくてはいけない.
関数引数に関する
オブジェクトの扱い方
関数へのobjectの引渡し:内容
• C,C++では値渡しが原則
– オブジェクトの値を渡す?
• オブジェクトそのままをコピーして渡
す.オブジェクトが大きいと大変.
• 一時的に,オブジェクトが作られる.
– コンストラクタが呼ばれる?
– コンストラクタは初期化用であり,今の
値とは違う.
– コピー・コンストラクタと言うものがある
• コンパイラーが影で名前をつける.
• 関数を抜けるときに廃棄される.
– デストラクタが呼ばれる.
– オブジェクトの代入と同じ
• 元のオブジェクトには影響が無い
• オブジェクトの中に動的なメモリが
あると問題がある.
関数へのobjectの引渡し:例
#include <iostream>
using namespace std;
class samp { int i;
public:
samp(int n) { i = n; }
int get_i() { return i; } };
int sqr_it( samp o )
{ return o.get_i() * o.get_i(); }
int main()
{
samp a(10), b(2);
cout << sqr_it(a) << "\n";
cout << sqr_it(b) << "\n";
return 0;
}
Objectの値渡し:例2
#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 << "しかし、a.iはmain()で変更されない: ";
cout << a.get_i(); return 0; }
オブジェクトのアドレス
• ポインタ変数
– 対象(オブジェクト,変数)の
アドレスを値として持つ変数
– オブジェクトの先頭アドレスを値として
持つ
– コンパイラはオブジェクト名から先頭ア
ドレスを取り出すことが出来る
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; }
オブジェクトの返しI
関数からの返り値も引数渡しと同じ?
• オブジェクトを返す時,オブジェクト
はコピーされるのか?
– 一時的なオブジェクトが作られる.
このオブジェクトは何時,開放さ
れるのか?
• 関数を抜けた時では,使えない.
• どこかに代入された後.
– デストラクタが呼ばれる.
この処理は判り難い.これに起因
するエラーは頻繁に起こる.
オブジェクトの返しII
#include <iostream>
#include <cstring>
using namespace std;
class samp { char s[80];
public:
void show() { cout << s << "\n"; }
void set(char *str) { strcpy(s, str); } };
samp input(){ char s[80]; samp str;
cout << "文字列の入力: ";
cin >> s;
str.set(s);
return str; }
int main(){
ob = input();
ob.show();
return 0; }
samp ob;
オブジェクトを返す時に起きるエラー
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
class samp { char *s;
public:
samp() { s = '\0'; }
~samp() { if(s) free(s);
//動的メモリ注意が必要
cout << "sを解放\n"; }
void show() { cout << s << "\n"; }
void set(char *str); };
void samp::set(char *str){
s = (char *) malloc(strlen(str)+1);
if(!s) { cout << "メモリ割り当てエラー\n";
exit(1); }
strcpy(s, str); }
デストラクタが3回呼ばれる.
samp input( ){
char s[80]; samp str;
cout << "文字列の入力: ";
cin >> s;
str.set(s);
return str; }
int main( )
{
samp ob;
ob = input(); //ここで 問題が発生する
ob.show();
return 0;
}
C++の作成手順
•
•
必要なClassを作成する.
•
•
空の場所だけを作る.
空の関数を作る.
Class間の取り決めを作る.
1. 必要な情報,変数を決める.
2. やり取りするメッセージを決める.
1. データ,命令,関数
•
関数の中身を記述する.
1. オブジェクトを生成する.
2. オブジェクト間の処理を書く.
•
オブジェクト間でのメッセージのやり取り
で処理が進んでいく.
手続き型のプログラム
1. 今までは,処理の流れを先に決める.
2. 次に,必要な変数,配列を決める.
3. 変数の値を取り出し,計算して,
4. 計算結果を変数に入れる.---これの繰り
返し,
5. 条件と制御:
– さらに,複数の条件でもプログラムが
動くように条件文を加える.
• 関数: 処理の流れの中で使われる.
変数を持ってくる.
計算結果を書き込む.
何処から変数を持ってきて,
何処にしまうか
C++の作成手順 II
•
•
•
•
•
処理中心から,データ中心に大きく変更
処理は,データ:オブジェクトの付属物.
オブジェクトが他のオブジェクトに作用.
オブジェクト間の関係,相互作用が重要
オブジェクト全体を大局的に決めれば,オ
ブジェクト相互の関係は局所的,
すなわち,局所的な変更の影響は
全体に及ばない.
オブジェクトをもっと自由に使う
やって見ることが大事.
慣れるまで,何でもクラスにできるか考えて
みる.
処理は後回し
クラスの関係に注目する.