• 検索結果がありません。

基本データ型 と クラス

N/A
N/A
Protected

Academic year: 2021

シェア "基本データ型 と クラス"

Copied!
35
0
0

読み込み中.... (全文を見る)

全文

(1)

1

クラス Class

(1)抽象データ型とクラス

上級プログラミング 講義資料 成蹊大学理工学部 情報科学科

(2)

2

基本データ型 クラス

基本データ型 [

int, double, char

など]

データのみの存在であり、これらに対する操作は、そ れが宣言されたスコープ内において自由にできる。操 作はもともと用意された演算子(+,-,*,/,%)などを通じ て行う。

クラス [

string, ifstream

など]

データ部分とそれを操作する関数をひとまとめにカプ セル化したもの。データ部分には、基本データ型や他 のクラスを用いることができる。

C++に既存のstringifstreamについては、利用者は その構造を知らなくても利用できている。逆にクラス を設計する立場では、「利用者が中身を知らなくても 安全に使えるように」設計する。

(3)

3

クラスの必要性

学生

10

人の氏名と英語・数学の試験の得点 を整理したい。配列を利用すると・・・

72 85 81 92 83

95 80 88 90 85 阿部 隆史

井上 裕樹 神田 美樹 佐藤 純子 田中 浩二

… … …

氏名 name[] 英語 e[] 数学 m[]

一人の学生に着目するために共通のインデックスを 使っているだけで、一人分としてまとまりがない

(4)

4

クラスの必要性

氏名と英語・数学の得点は、一人の学生が持 つデータとして整理すべき。

72 85 81 92

95 80 88 90 阿部 隆史

井上 裕樹 神田 美樹 佐藤 純子

配列と違って、異なるデータをひとつにまとめる 必要がある。

(5)

5

オブジェクト指向

オブジェクト中心の「もの」の見方

「もの」には、その「もの」自身を性格付け しているものがある。

 属性:「もの」が持つデータ

 振る舞い:「もの」が持つ機能

例:電気ポット

属性:容量、水量、湯温 振る舞い:お湯を沸かす

(6)

オブジェクトの設計=クラス

◎オブジェクト(対象物)=インスタンス

「対象」の機能(メンバ関数)を見極める。

「対象」の機能を実現するのに必要な

データ(データメンバ)を見極める。

最初は、あまり内部の詳細にとらわれず、表面的な性格 を大きく捉えるのがコツ。

クラスの利用者がクラス内部の詳細を知らなくても使える ように設計する。

(7)

7

オブジェクトの例

二次元平面上の点

点 B Y

O X

点 A

各点の固有のデータは、

名前・x座標・y座標 新しく点を作る

点を移動する

点の座標を表示する

属性 機能

機能

機能

(8)

クラスの定義の仕方

class クラス名{

private: // 省略してもpublicまでのメンバはすべてprivateになる

public:

};

メンバの宣言(非公開部)

主にデータメンバ

メンバの宣言(公開部)

主にメンバ関数の定義

メンバ関数の定義は、上記の中ではプロトタイプ宣言のみにし、クラスの定 義のあとに、クラススコープ演算子::を用いて、関数定義を行うこともできる。

上のようにメンバ関数定義をクラス定義内で行うと、その関数はデフォルト でインライン関数となる。

(9)

9

例:二次元平面の点を表すクラスを定義する。

#include <iostream>

#include <string>

#include <iomanip>

using namespace std;

class Point { // 以下はクラスPointの定義 private: // このprivateは省略可

string nm; // 点の名前

int x, y; // x座標とy座標 public:

Point(); // 点を作る

void set(string, int, int); // 点のデータを設定する void move(int, int); // 点を移動する

void print() const; // 点の座標を表示する };

Point::Point(){ nm="noname"; x=0; y=0; }

void Point::set(string n0, int x0, int y0){ nm=n0; x=x0; y=y0; } void Point::move(int dx, int dy){ x+=dx; y+=dy; }

void Point::print() const{

cout <<"(" <<nm <<", " <<setw(2) <<x <<", "

<<setw(2) <<y <<")¥n";

}

(10)

クラスのメンバ関数と普通の関数の違い

例:円の面積を求める関数

double circle_area(double r){

return r*r*M_PI;

} // 普通の関数 class Circle{

private:

double r;

public:

double area();

};

double Circle::area(){ return r*r*M_PI; }

仮引数は、その関数に 必要なデータを渡す

ためのもの

メンバ関数は、そのオブジェクト

(クラス)のデータメンバを引数と して受け取らなくても使用するこ

とができる。

(11)

11

オブジェクト(インスタンス)の宣言

クラス名 オブジェクト名;

例: Circle A, B; // Aと円Bの宣言

メンバ関数の呼び出し

オブジェクト名.メンバ関数名 () ;

例: double sa=A.area(); // Aの面積 double sb=B.area(); // Bの面積

(12)

例:二次元平面の点を表すクラスを利用する

/*******************************

四角形の座標表示、移動関数

*******************************/

void move(Point rect[], int sz, int dx, int dy) {

for(int i=0; i<sz; i++) rect[i].move(dx, dy);

}

void print(Point rect[], int sz) {

cout <<"rectangular ----¥n";

for(int i=0; i<sz; i++){

cout << "¥t";

rect[i].print();

} }

【続く】

プログラム例1(続き)

(13)

13

例:二次元平面の点を表すクラスを利用する

/*************************

mainプログラム

四角形を作って移動する

*************************/

int main() {

const int sz=4;

Point r[sz];

print(r, sz);

r[0].set("A", 0, 0);

r[1].set("B",20, 0);

r[2].set("C",20,10);

r[3].set("D", 0,10);

print(r, sz);

move(r, sz, 10, 5);

print(r, sz);

move(r, sz, 10, 5);

print(r, sz);

return 0;

}

プログラム例1(続き)

rectangular ----

(noname, 0, 0) (noname, 0, 0) (noname, 0, 0) (noname, 0, 0) rectangular ----

(A, 0, 0) (B, 20, 0) (C, 20, 10) (D, 0, 10) rectangular ----

(A, 10, 5) (B, 30, 5) (C, 30, 15) (D, 10, 15) rectangular ----

(A, 20, 10) (B, 40, 10) (C, 40, 20) (D, 20, 20) 実行結果

(14)

例:名前と年齢をデータメンバとし、このデータメンバにデータを入 力する関数inputとデータを出力する関数printをメンバ関数とす るクラスPersonの定義

class Person{ // 以下はクラスPersonの定義 private:

string name; // 名前 int age; // 年齢 public:

void input(); // 名前と年齢の入力(プロトタイプのみ)

void print(); // 名前と年齢の出力(プロトタイプのみ)

};

void Person::input() // 入力用メンバ関数の定義 {

cout << "名前と年齢は? ";

cin >> name >> age;

}

void Person::print() // 出力用メンバ関数の定義 {

cout << "名前:" << name << " 年齢:" << age << endl;

}

(15)

15

int main() {

Person singer, writer; // オブジェクトの宣言 singer.input(); // メンバ関数の呼び出し

writer.input();

singer.print();

writer.print();

return 0;

}

実行例

comsv1% example1

名前と年齢は? Hayashi 19 名前と年齢は? Kawai 20 名前:Hayashi 年齢:19 名前:Kawai 年齢:20 comsv1%

(16)

class Person{

string name; // 名前 int age; // 年齢 public:

void input(){ // 名前と年齢の入力 cout << 名前と年齢は? ”;

cin >> name >> age;

}

void print(){ // 名前と年齢の表示

cout << "名前:" << name << " 年齢:" << age << endl;

} };

クラス定義のメンバ関数をプロトタイプ宣言だけでなく、関数の定義 も記述する例

メンバ関数を上のように定義すると、それらはインライン関数となる。

インライン関数にすると、実行ファイルサイズは大きくなるが実行速度は速くなる。

関数の内容がよほど簡単な場合以外はメンバ関数はクラス定義の外部で 記述する方が良い。

(17)

17

カプセル化(encapsulation

データとそれらに対する操作を1つの構造にカプセル化

クラスはオブジェクト指向プログラミングにおいて重要な概念

データ3データ2データ1

操作1

操作2

クラスの中以外の場所で宣言されるint型やdouble型のような基 本データ型の変数は、クラスのオブジェクトと比較するとデータ のみの構造であり、publicと同様の扱いであるため、その変数 のスコープ内ではどこでも自由にアクセスできるようになってい る。従って情報隠蔽の立場では非常に弱い状態にあると言える。

カプセル化(encapsulation

操作1

操作2

メンバをprivateあるいはpublicにすることによって隠蔽したり 公開したりすることができる。

(18)

Person クラスの例

Person singer, writer;

singer

input print

writer

input print

中の構造(privateメンバ)は不明だが、使い方は 用意されたボタン(publicメンバ)で分かる

(19)

19

クラスの定義とオブジェクトの宣言

クラスPerson 名前

年齢

名前と年齢の取得 名前と年齢の表示

歌手 singer 名前

年齢

名前と年齢の取得 名前と年齢の表示

作家 writer 名前

年齢

名前と年齢の取得 名前と年齢の表示 クラス定義

オブジェクト宣言 をすると...….

設計図のみで実体はない

メモリ中に実体ができる データメンバ

メンバ関数

(20)

メンバの属性とアクセス権

privatepublicの違い

privateメンバ

name age

publicメンバ

input() print() Personクラスのオブジェクト singer

Personクラスの他のオブジェクト

のメンバ関数 search()

Personとは別のクラス

や関数の中

func()やmain()など アクセス不可

singer.input() singer.print()

これらの呼び出しはどこからでも可

(21)

21

int main() // このmainではコンパイルしてもエラー

{

Person singer, writer; // オブジェクトの宣言 singer.input(); // メンバ関数の呼び出し writer.input();

singer.print();

writer.print();

singer.print();

return 0;

}

singer.age++;

privateメンバへの不正アクセスの例

正しくは、Personクラスのpublicメンバ関数に年齢ageをインクリメントする 関数を準備し、それをこのmain関数の中から呼び出すようにすればよい。

void Person::happyBirthday(){ age++; }

singer.age++; // main関数からはsinger.ageに // 直接アクセスできない

singer.happyBirthday();

(22)

① データメンバは

private

にする(方が良い)。

② データメンバに直接アクセスできるメンバ関 数を

public

で準備する。

③ データメンバは必要最小限を準備し、それら の加工により得られる情報のためにメンバ 関数を準備する。

クラスを定義する場合のポイント(1)

(23)

23

クラスを定義する場合のポイント(2)

そのクラスのオブジェクトの使われ方から先に 考える

例:複素数のクラスComplexを作るとして、

その使われ方は・・・

int main() {

Complex x,y,z;

x.input(); // 複素数xの入力 y.input(); // 複素数yの入力

z= x.add(y); // xにyを加えた複素数をzに代入 z.print(); // zを表示

cout << endl;

return 0;

}

(24)

24

クラス定義の例

class Complex {

double real, imaginary; // 実数部realと虚数部imaginary public:

void input();

void print();

Complex add(Complex);

};void Complex::input(){

cout << "実数部と虚数部を入力せよ-->";

cin >> real >> imaginary;

}void Complex::print(){

cout << real;

if(imaginary>0) cout << "+";

cout << imaginary << “i”; // a+biまたはabiの形式で表示 }Complex Complex::add(Complex k){

Complex sum;

sum.real=real+k.real;

sum.imaginary=imaginary+k.imaginary;

return sum;

}

(25)

25

コンストラクタとデストラクタ

コンストラクタ(constructor

クラス名と同名のメンバ関数

オブジェクト生成時に自動実行される関数。

オブジェクトのデータメンバの初期化が主な目的。

(データメンバ変数は、宣言時に初期化できないから)。

リターン型を持たない。

多重定義すれば、色々な方法でオブジェクトを初期化可能。

明示的に呼び出すと、新しいオブジェクトを生成する。

デストラクタ(destructor

クラス名の前に(tilde:チルダ)を付けた名前のメンバ関数 – オブジェクト消滅時に自動実行される関数。

リターン型を持たない。

– 多重定義はできない。

(26)

26

コンストラクタの例(1)

class Complex {

double real, imaginary; // 実数部realと虚数部imaginary public:

Complex(double a=0, double b=0){ // デフォルト引数で

// 実数部虚数部ともに0にする準備 real=a; // 初期値を与えられたらそれを使ってデータメンバを初期化 imaginary=b;

}

// ・・・ 中略 ・・・

Complex add(Complex k){

return Complex(real+k.real, imaginary+k.imaginary);

} // コンストラクタでリターン値用のオブジェクトを構成することができる };

int main() {

Complex x(4,7), y(2), z; // yの虚数部はデフォルト引数により0, // zも同様に0+0iとなる

// ・・・ 後略 ・・・

(27)

27

コンストラクタとデストラクタの例(2)

#include <iostream>

#include <string>

class Person { string name;

int age;

public:

Person(string tmp=“”, int y=20){ // コンストラクタ name=tmp; age=y;

cout << "Object " << name << " has been generated.¥n";

}

~Person(){ // デストラクタ

cout << "Object " << name << " is released...¥n";

}

void print(){

cout << "Name : " << name << endl;

cout << "Age : " << age << endl;

} };

(28)

int main(void) {

Person a(Ken,21), b(Ryo,18);

// この生成の際にそれぞれのコンストラクタが実行され a.print();

b.print();

{ // 内部ブロックIB

Person a(Aya); // 新オブジェクトaの生成と、コンストラクタの実行 a.print();

b.print();

} // このブロック終端でaが消滅され、その際にデストラクタが実行される a.print();

b.print();

} // main関数の終了においてa,bが消滅されるが、その際にそれぞれ // デストラクタが実行される

(29)

29

実行結果

cserv1/home/usrX/us052999/advpro% example3

Object Ken has been generated. main関数ブロックでのaの生成時 Object Ryo has been generated. ← main関数ブロックでのbの生成時 Name : Ken

Age : 21 Name : Ryo Age : 18

Object Aya has been generated. ← 内部ブロックIBでのaの生成時 Name : Aya

Age : 20 Name : Ryo Age : 18

Object Aya is released... ← 内部ブロックIB終了時におけるaの消滅 Name : Ken

Age : 21 Name : Ryo Age : 18

Object Ryo is released... ← main関数ブロック終了におけるbの消滅時 Object Ken is released... ← main関数ブロック終了におけるaの消滅時

(30)

const メンバ関数

ユーザ定義のクラスAtypeがあるとする。このとき、

const Atype x;

x.func();

のように呼び出されるAtypeのメンバ関数func()constメンバ関 数でなければならない。すなわち、

リターン型 func(引数1, ・・・) const { ・・・・・ }

のように記述しなければならない。

例: double func() const { return data*10; }

データメンバdataを10倍して返すだけなので、funcはconstメンバ関数 と明示した方が良い。なぜならばデータメンバを変更することがないこ とをコンパイラにもはっきり示すことができるからである。

constメンバ関数にしておけば、constオブジェクトからだけでなく、

constオブジェクトからも呼び出すこともできる。しかし逆はでき

ない(C++の仕様)。

(31)

31

メンバ関数の作成方法

1. メンバ関数の利用場面を注意深く見る。

2. 関数の引数とリターン値に注目してプロトタイプを 決める。

3. 引数省略の関数呼び出しには、デフォルト引数の 利用を考慮する。

4. そのオブジェクト自身のデータメンバは、メンバ関 数の引数にしなくても、関数本体で使用できる。

5. データメンバおよび受け取る引数から結果を求め る処理の流れを決定する。

(32)

32

1.メンバ関数の利用場面を注意深く見る

int main() {

Point P(10, 0), Q, M;

P.print(); cout << endl;

Q.set(0, 5.5);

Q.move(0, 4.5);

double d=P.distance(Q);

cout << "PQ=" << d << endl;

M=P.middle(Q);

cout << "PM=" << P.distance(M) << " QM=" <<

Q.distance(M) << endl;

}

(33)

33

2.引数と返値に注目してプロトタイプを決定

int main() {

Point P(10, 0), Q, M;

P.print(); cout << endl;

Q.set(0, 5.5);

Q.move(0, 4.5);

double d=P.distance(Q);

cout << "PQ=" << d << endl;

M=P.middle(Q);

cout << "PM=" << P.distance(M) << " QM=" <<

Q.distance(M) << endl;

}

オブジェクト宣言時の引数はコンストラクタ に渡す。double型を2つ。

プロトタイプ Point(double, double);

コンストラクタ呼び出し時に引数なしの場合 あり。その時は各々0とみなす。

プロトタイプ Point(double=0, double=0);

引数なし、返値なし。

プロトタイプ void print();

引数double型2つ、返値なし。

プロトタイプ void set(double, double);

引数double型2つ、返値なし。

プロトタイプ void move(double, double);

引数Point型1つ、返値double型。

プロトタイプ double distance(Point);

引数Point型1つ、返値Point型。

プロトタイプ Point middle(Point);

(34)

Point クラスの定義

class Point{

private:

double x, y; // x座標とy座標

public:

Point(double=0, double=0); // コンストラクタ

void print();

void set(double, double);

void move(double, double);

double distance(Point);

Point middle(Point);

};

(35)

35

Point クラスのメンバ関数の定義

Point::Point(double a, double b){ x=a; y=b; } void Point::print(){

cout << "(" << x << "," << y << ")";

}

void Point::set(double a, double b){ x=a; y=b; }

void Point::move(double a, double b){ x+=a; y+=b; } double Point::distance(Point G){

return sqrt((x-G.x)*(x-G.x)+(y-G.y)*(y-G.y));

}

Point Point::middle(Point G){

return Point((x+G.x)/2, (y+G.y)/2);

}

自分自身のデータメンバは引数にしな くても使用できる。

自分と他のオブジェクトを明確に区別する。

自分を中心に考えるのがコツ。

クラス引数の例

中点を作って返す、ということなのでコンストラクタ が活躍。

参照

関連したドキュメント

、肩 かた 深 ふかさ を掛け合わせて、ある定数で 割り、積石数を算出する近似計算法が 使われるようになりました。この定数は船

基準の電力は,原則として次のいずれかを基準として決定するも

いてもらう権利﹂に関するものである︒また︑多数意見は本件の争点を歪曲した︒というのは︑第一に︑多数意見は

 貿易統計は、我が国の輸出入貨物に関する貿易取引を正確に表すデータとして、品目別・地域(国)別に数量・金額等を集計して作成しています。こ

基準の電力は,原則として次のいずれかを基準として各時間帯別

汚染水処理設備,貯留設備及び関連設備を構成する機器は, 「実用発電用原子炉及びその

それらのデータについて作成した散布図を図 15.16 に、マルチビームソナー測深を基準に した場合の精度に関する統計量を表 15.2 に示した。決定係数は 0.977

・LNG を輸出港等からヨーロッパの LNG 基地に輸送し、再積み出しするためのコストを試算。ケース