Issuu on Google+

C++プログラミング資料 機械情報工学科 山根 克 e-mail: yamane@ynl.t.u-tokyo.ac.jp

1 C++の概要

}; // セミコロンが必要 クラス内で定義された関数,変数をそれぞれそのクラ

1.1 文法上の主な違い

スのメンバ関数,メンバ変数という.クラスの宣言は通常

• /* */のほか,//から改行までコメントとして処理され

ヘッダファイルに書く.関数の実体は上の SmallFunc() の

る.

ようにクラス内に書いてもよいし,以下のように別のソー スファイルに書いてもよい.

• 名前が同じでも引数が違えば別の関数になる.すな わち,以下の 2 つの関数を同時に定義することがで

int A::BigFunc() {

きる.これを関数のオーバーロードという.ただし,

var = sin(2.0) * exp(1.5); return 0;

返り値だけが異なる複数の関数を定義することはで きない.

}

void Func() {

クラスの宣言は C の構造体と似ているが,メンバ関数

...

を定義できる,メンバがデフォルトで private になる (詳し

}

くは後述) などの違いがある.構造体もほぼ C と同様に使

void Func(int arg) { ...

// 引数あり

え,メンバ関数も定義できる. ファイル名はなるべくその内容と関連を持たせる.例え

}

ば,クラス A の宣言を記述したヘッダファイルは A.h,A のメンバ関数の実体を記述したソースファイルは A.cpp な

• 変数は以下のようにどこでも宣言できる.

どとする.

int Func() {

メンバ関数からは同じクラスのメンバ関数・メンバ変数

int sum = 0; for(int i=0; i<10; i++)

を自由に呼び出したり,参照したりすることができる.ク

sum += i; int result = sum / 2;

するには以下のようにする.

ラスの外からクラスのメンバ関数・メンバ変数にアクセス

A a var; a var.SmallFunc();

return result; }

a var.var = -1.0; A(), ˜A() はそれぞれクラスのオブジェクトを生成・破壊 するときに呼ばれる関数で,コンストラクタ・デストラク

1.2 クラス

タという.関数名はそれぞれ “クラス名 (),” “˜クラス名 ()”

クラスは以下のようにして宣言する.

にしなければならず,値を返すことはできない.また,デ

class A {

ストラクタは引数を取ることができない.通常,コンスト

public: A();

ラクタには変数を初期化する処理,デストラクタにはメモ

˜A();

// コンストラクタ

リを解放する処理をそれぞれ記述する.

// デストラクタ

1.3 継承

int SmallFunc() { var = 5.0;

あるクラスをもとに,別の新しいクラスを作ることを継

return -1;

承といい,もとのクラスを基本クラス,新しいクラスを派

}

生クラスという.基本クラスのメンバ関数・変数は自動的

int BigFunc();

に派生クラスにも引き継がれる.下はクラス A を継承す

double var;

る派生クラス B を定義した例である.

1


class A {

public:

public: A();

int PublicFunc(); protected:

˜A(); int a func();

int protected func(); }; class B: public A { B();

double var; }; class B: public A { B();

// A を継承

˜B(); int Func(); };

˜B(); int b func() { var = 3.0;

// A のメンバも参照可能

上のようにクラス A, B を定義したとき,以下のような挙

a func(); return 0;

動になる.

}

A a var;

}; 派生クラスでは,基本クラスのメンバ関数を定義しなお すこともできる.これを関数のオーバーライドという. 派生クラスのコンストラクタの中で基本クラスまたはメ ンバ変数のコンストラクタを呼ぶときは次のように書く.

a var.PublicFunc();

// OK

a var.private func(); a var.protected func();

// NG // NG

int B::Func() { PublicFunc();

// OK

protected func(); private func();

B::B() : A() { ...

// OK // NG

}

} なお,派生クラスのデストラクタでは基本クラスのデスト

1.5 friend

ラクタが自動的に呼ばれる.

あるクラスと別のクラスがフレンドクラスであれば,pri-

1.4 public / protected / private

vate や protected な関数・変数も相互に参照することがで きる.両方のクラスで互いに相手がフレンドであると宣言

メンバ関数・変数へのクラス外からのアクセスを制限す

する必要がある.

るために,クラス宣言の中で使う.次に上記のいずれかが 現れるまで有効で,デフォルトは private. 構造体の場合は

class A {

デフォルトで public になる.

friend class B; ...

• public: このあとに宣言されたメンバ関数・変数はど こからでもアクセスできる.

private: double var;

• private: このあとに宣言されたメンバ関数・変数は他 のクラスからはアクセスできない.

... }; class B {

• protected: このあとに宣言されたメンバ関数・変数は, このクラスを継承したクラスからのみアクセスできる.

friend class A; ...

これによって,外部からの不正な操作を防ぐことができ

int Func(A& a obj) { a obj.var = 0.5; // フレンドなので OK

る.以下に例を示す.

return 0;

class A { A();

} ...

˜A();

};

int private func(); 2


1.6 オペレータ (演算子)

int n = 10; c func1(n); c func2(&n);

クラスの中では+, -などのオペレータ (演算子) をメンバ として定義することができる.

/* n は 10 のまま */ /* n は 11 になる */

}

class A { ...

これに対し C++では,参照を使うことによってポイン タを使わなくとも引数の値を変化させることができる.特

double var;

に,引数がクラスのオブジェクトの場合,参照にしておく

... A operator + (A& a1) {

といちいちコピーされなくなるため,高速になる.

A ret; ret.var = var + a1.var;

// C++ void cpp func(int& a) { a = a + 1;

return ret; }

}

};

int main() { int n = 10; cpp func(n);

1.7 標準出力,標準エラー出力

// n は 11 になる

}

C ではコンソールに文字列などを出力するのに printf() 関数を使っていた.C++ではこれに加えて出力ストリーム を使うことができる.以下の 2 つの例はほぼ同じ出力にな

const

る (フォーマットが多少異なる可能性がある).

変化させるようなコードを書くとコンパイラがエラーを出

引数・変数宣言の前に const をつけると,その値を

す.値を変化させたくない引数や変数があるときに安全性

/* C */ int n;

が高まる.

int Func(const A& a obj) { // NG a obj.var += 0.1;

double d; printf(”integer: %d, double: %f\n”, n, d);

return 0; // C++

}

#include <iostream> using namespace std; cout << ”integer: ” << n

new, delete C における malloc, free に対応する演算子で, はるかに使いやすい.以下に示す例は,いずれも整数 5 個

<< ”double: ” << d << endl;

の配列を確保し,その後解放している.

/* C */ int* int array = malloc(sizeof(int)*5);

ファイルストリームクラスを使うと,ファイルへの出力 も同様の書式でできる.

... free(int array);

1.8 変数関連

// C++

参照

C では,関数の中で引数の値を変えるためにはポイ ンタを引数とする必要があった.

int* int array = new int [5]; ...

/* C */

delete[] int array;

void c func1(int a) { a = a + 1;

スコープ

} void c func2(int* a) {

関数・変数の使える範囲.同じ場所で同じ名前

の関数・変数が使える場合は,C++の規則に従ってどちら が参照されるかが決まる.それ以外の関数・変数を使いた

*a = *a + 1;

い場合はスコープ解決演算子::を使って指定する.

}

class A { int main() {

... 3


クラスのメンバ関数だけでなく,グローバル関数でも同様

public: int MemberFunc(); int var; // メンバ変数

にデフォルト引数を使うことができる.デフォルト引数は

...

後にデフォルト値を持たない引数を定義することはできな

いくつでも使えるが,以下の例のようにデフォルト引数の いことに注意する.

}; class B { ...

int func1(int arg1 = -1, int arg2); int func1(int arg1 = -1, int arg2 = 1);

public: int MemberFunc(); // オーバーロード ...

2

};

// NG // OK

ポインタ

2.1 ポインタの基本 // グローバル変数

int var; int Func() { int var; ::var = 10;

// ローカル変数 // グローバル変数

var = -10;

// ローカル変数

例えば,整数へのポインタは以下のようにして宣言する.

int* p num; ポインタ変数 (上の例では p num) にはその変数が記憶さ れているメモリ空間中のアドレス (番地) が入っており,実

}

際の値を参照するには*p num と書く必要がある.

int A::MemberFunc() { ::var = 10; // グローバル変数 var = -20; return 0;

上の例ではポインタが宣言されただけで,実際に整数を 格納する領域は確保されていない.この時点での p num

// メンバ変数

の値はメモリの初期状態で決まる意味のない値になってい るので,いきなり

} int B::MemberFunc() {

*p num = 10;

A::MemberFunc(); // A のメンバ関数

とするとエラーを起こす.領域を確保するには,前述の

return 0;

new 演算子を使う.

}

p num = new int; デフォルト引数

*p num = 10;

関数のプロトタイプ宣言である引数に対

してデフォルトの値を定義しておくと,呼び出し時にその

領域を確保していないポインタに代入するミスをなくす

引数を省略できる.例えば以下のように宣言・定義された

ため,ポインタを宣言するときは必ず NULL (0) で初期化

関数を考える.

し,代入前に 0 以外のアドレスが入っているかどうかを確

// A.h

認するようにするとよい.

class A {

int* p num = 0; ...

... int func(int arg1, int arg2 = -1);

p num = new int; ...

}; // A.cpp

if(p num)

// ここではデフォルトの値を書かないことに注意 int A::func(int arg1, int arg2) {

*p num = 10;

return (arg1 + arg2); }

2.2 クラスへのポインタ

このとき,以下のどちらの呼び出しも可能である.

クラスへのポインタも同様に定義することができる.

// main.cpp A a obj;

class A { ...

a obj.func(5, 2);

// 結果は 7

a obj.func(5);

// 結果は 4

public: int Func(); 4


p

double var; ...

20

0

};

60 130 210 270

20 100

A* p a = 0;

200

p[0][0]

p[0]

p[0][9]

60 28 -10 4

99

... 199

130

210

270

299

ポインタで宣言されたクラスのメンバ関数・変数は以下の ようにしてアクセスする.

Figure 1: Example of the memory allocation for a twodimensional array.

p a = new A; p a->Func(); p a->var = 10.0;

A* p a = new A; B* p b = new B;

2.3 配列との関係

このとき,p b->Func(); とすると当然 B::Func() が呼ばれ

大きさの決まった配列は以下のようにして宣言する.

る.これに対し,次のようにしたとき

A* p a = 0; p a = (A*)new B;

int int array[10]; これは,以下のようにした場合と全く同じである.

すなわち,派生クラスへのポインタを基本クラスへのポイ

int* int array;

ンタに代入したとき,p a->Func(); とすると A::Func() が

int array = new int [10];

呼ばれてしまう.実際に生成されたクラスである B のメン

Microsoft Visual C++では,前者は配列の大きさが定数で なければならないのに対し,後者では [] の中が変数でも

バ関数が呼ばれるようにするためには,A または B のク ラス宣言で Func() の宣言を以下のように変更すればよい.

構わない (g++ではどちらでも変数が使える).

virtual int Func();

配列を表す変数 int array には,実は配列の先頭要素が 記憶されたメモリのアドレスが入っている.従って,配列

基本クラスで virtual 関数として宣言すると,それを継承

とポインタはほとんど同じものであるということができ

するクラスでも自動的に virtual 関数となる.

る.要素の参照方法は,配列と考えた場合とポインタと考 えた場合とで以下の 2 通りがある.以下の 2 つの例は,い

2.5 ポインタのポインタ

ずれも int array の第 5 要素を参照している.

ポインタの実際の値はアドレスであると上に書いたが,

int array[4] = -1; *(int array+4) = -1;

そのアドレスを記憶するためのメモリ領域のアドレスを 考えることもできる.すなわち,ポインタのポインタであ る.ポインタのポインタは二次元配列に対応している.

2.4 virtual 関数

int p[4][10]; p[0][0] = 28;

以下のような例を考える.

p[0][1] = -10;

class A {

p[0][2] = 4; ...

... int Func(); ...

上記のように二次元配列を宣言したときのメモリ空間上に

};

おける配置例を図 1 に示す.p[0]–[3] はポインタであり,

class B: public A { ...

それぞれ整数 10 個の配列の先頭アドレス (上の例では 60,

int Func(); ...

タの配列の先頭アドレス (上の例では 20 番地) が入ってい

130, 210, 270 番地) が入っている.p そのものにはポイン るので,p はポインタのポインタとなる.

};

5


C++プログラミング資料 サンプルソースコード

71

* virtual 関数にする * 関数宣言の後の const はその関数が変数を書き換えないこと を示す */ virtual MemberType GetMemberType() const { return OTHER_TYPE; } /** * オペレータ定義の例 */ // 同じ人かどうか bool operator == (LabMember& _member); // データ書き出し friend ostream& operator << (ostream& ost, const LabMember& _member);

73 74 75 76 1 2 3 4 5

77

/* * lab.h: header file for sample C++ program * Create: Katsu Yamane, 03.07.16 */

6 7 8 9 10 11 12 13

// string クラス #include <string> // コンソール入出力クラス #include <iostream>

18

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

#define MAX_MEMBERS 50 #define MAX_OLD_MEMBERS 200

38 39 40 41 42 43 44 45 46 47

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

87 88 89

91

93 94

96 97 98

protected: /** * 名前 */ string name;

101 102 103 104 105 106 107 108 109 110 111

113 114 115 116 117 118 119

/** * class Staff: スタッフメンバのためのクラス * LabMember クラスを継承 */ class Staff : public LabMember { public: /** * 肩書き (教授,助教授,講師,助手,技官,秘書,その他) */ enum Title { PROFESSOR, ASSOCIATE_PROFESSOR, LECTURER, RESEARCH_ASSISTANT, TECHNICIAN, SECRETARY, OTHER_TITLE, };

120 121 122 123 124

*/ virtual ˜LabMember(); /** * protected, private メンバにアクセスするための関数 * const の使い方に注意 */ const string& GetName() { return name; } const string& GetDescription() { return description; } void SetName(const string& _name) { name = _name; } void SetDescription(const string& _description) { description = _description; } /** * メンバの種類 (スタッフ,学生,その他) を返す

private: /** * その他のメンバ (ビジターなど) のための備考 */ string description; };

99 100

112

/** * デフォルトコンストラクタ */ LabMember(); /** * 名前を指定するコンストラクタ */ LabMember(const string& _name); /** * デストラクタ * 派生クラスのデストラクタを呼びたいので virtual 関数にす

37

49

86

95

/** * class LabMember: 研究室メンバの基本クラス */ class LabMember { // フレンドクラス (Lab クラスの定義を参照) friend class Lab; public: /** * メンバの種類 (スタッフ,学生,その他) */ enum MemberType { STAFF, STUDENT, OTHER_TYPE, };

36

48

84

92

19 20

83

90

using namespace std;

16 17

80 81

85

14 15

78 79

82

#ifndef __LAB_H__ #define __LAB_H__

派生クラスはそれぞれ自分の種類を返さなければならないの

70

72

1 lab.h

*

69

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

1

/** * デフォルトコンストラクタ */ Staff(); /** * 名前と肩書きを指定するコンストラクタ */ Staff(const string& _name, Title _title); /** * デストラクタ */ ˜Staff(); /** * protected, private メンバにアクセスするための関数 */ Title GetTitle() { return title; } void SetTitle(Title _title) { title = _title; } /** * メンバの種類 (学生) を返す


144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

*/ 219 void SetThesisTitle(const string& _thesis_title) { MemberType GetMemberType() const { 220 thesis_title = _thesis_title; return STAFF; 221 } } 222 /** // データ書き出し 223 * メンバの種類 (学生) を返す friend ostream& operator << (ostream& ost, const Staff& 224 _staff); */ 225 MemberType GetMemberType() const { private: 226 return STUDENT; /** 227 } * 肩書き 228 // データ書き出し */ 229 friend ostream& operator << (ostream& ost, Title title; 230 const Student& _student); }; 231 232 protected: /** 233 /** * class Student: 学生メンバのためのクラス 234 * 学年 * LabMember クラスを継承 235 */ */ 236 Grade grade; class Student 237 /** : public LabMember 238 * 指導教官 { 239 */ public: 240 Staff* advisor; /** 241 /** * 学年 (B4 から D3,その他) 242 * 卒業,修士,博士論文のタイトル */ 243 */ enum Grade { 244 string thesis_title; 245 B4, M1, 246 }; M2, 247 D1, 248 class Lab D2, 249 { D3, 250 // フレンドクラス (LabMember クラスの定義を参照) OTHER_GRADE, 251 friend class LabMember; }; 252 public: 253 /** /** 254 * デフォルトコンストラクタ * デフォルトコンストラクタ 255 */ */ 256 Lab(); Student(); 257 /** /** 258 * 研究室名を指定するコンストラクタ * 名前と学年を指定するコンストラクタ 259 */ */ 260 Lab(const string& _name); Student(const string& _name, Grade _grade); 261 /** /** 262 * デストラクタ * デストラクタ 263 */ */ 264 ˜Lab(); ˜Student(); 265 /** /** 266 * メンバを追加する * protected, private メンバにアクセスするための関数 267 * 追加されたメンバへのポインタを返す */ 268 */ Grade GetGrade() { 269 LabMember* AddMember(const string& name); return grade; 270 Staff* AddStaff(const string& name, Staff::Title title); } 271 Student* AddStudent(const string& name, Staff* GetAdvisor() { 272 Student::Grade grade); return advisor; 273 /** } 274 * メンバを現役から OB へ移す const string& GetThesisTitle() { 275 */ return thesis_title; 276 int RemoveMember(const string& name); } 277 /** void SetGrade(Grade _grade) { 278 * メンバ数,OB 数を返す grade = _grade; 279 */ } 280 int NumMembers() { int SetAdvisor(LabMember* _advisor) { 281 return n_members; // 指定されたメンバが講師以上かどうかを確認 282 } if(_advisor->GetMemberType() == STAFF && 283 int NumOldMembers() { ((Staff*)_advisor)->GetTitle() <= Staff::LECTURER) 284 return n_old_members; { 285 } advisor = (Staff*)_advisor; 286 /** return 0; 287 * 研究室名を取得,設定 } 288 */ // 講師以上でなかったか学生だったので設定できない.-1 を返 289 const string& GetName() { す. 290 return name; cerr << "error: invalid advisor for " << name << endl;291 } return -1; 292 void SetName(const string& _name) { } 293 name = _name;

2


} /** * 名前からメンバを探す * 見つかったらメンバへのポインタを返す * 見つからなかったときは 0(NULL) を返す */ LabMember* FindMember(const string& name); /** * 何番目のメンバかを返す * 見つからなかったときは-1 を返す */ int MemberIndex(LabMember* _member); // データ書き出し friend ostream& operator << (ostream& ost, const Lab& lab);

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

310 311 312 313 314 315 316 317 318 319 320

private: /** * AddMember, AddStaff, AddStudent の下請け * 失敗したら-1 を返す */ int add_member(LabMember* _member); /** * RemoveMember の下請け * 失敗したら-1 を返す */ int remove_member(LabMember* _member);

321

323 324 325 326 327 328 329 330 331 332 333 334 335

} // 見つからなかったら-1 を返す return -1;

30 31 32

}

33 34 35 36 37 38 39 40

42 43 44 45

LabMember* Lab::AddMember(const string& name) { LabMember* _member = new LabMember(name); int ret = add_member(_member); // ret が 0 以外のときは失敗 if(ret) { delete _member; return 0; } return _member; }

46 47 48 49 50 51 52

54 55 56 57

Staff* Lab::AddStaff(const string& name, Staff::Title title) { Staff* _staff = new Staff(name, title); int ret = add_member(_staff); if(ret) { delete _staff; return 0; } return _staff; }

58 59 60 61 62 63 64 65 66 67

};

68

#endif

70

69

337 338

28 29

53

/** * 研究室名 */ string name; /** * 現役メンバ */ LabMember* members[MAX_MEMBERS]; int n_members; /** * OB */ LabMember* old_members[MAX_OLD_MEMBERS]; int n_old_members;

322

336

27

41

309

// ポインタが一致したらそのインデックスを返す if(members[i] == _member) return i;

26

Student* Lab::AddStudent(const string& name, Student::Grade grade) { Student* _student = new Student(name, grade); int ret = add_member(_student); if(ret) { delete _student; return 0; } return _student; }

71 72 73

2 lab.cpp

74 75 76

1 2 3 4

/* * lab.cc: source file for sample C++ program * Create: Katsu Yamane, 03.07.16 */

80

#include "lab.h"

82

5 6

9 10 11 12 13 14 15 16 17 18 19

83

LabMember* Lab::FindMember(const string& name) { int i; for(i=0; i<n_members; i++) { // 名前が一致したらそのポインタを返す if(members[i]->name == name) return members[i]; } // 見つからなかったら 0 を返す return 0; }

20 21 22 23 24 25

79

81

7 8

77 78

84 85 86 87 88 89 90 91

int Lab::RemoveMember(const string& name) { LabMember* _member = FindMember(name); if(!_member) return -1; return remove_member(_member); }

92 93 94 95 96

int Lab::MemberIndex(LabMember* _member) { int i; for(i=0; i<n_members; i++) {

int Lab::add_member(LabMember* _member) { // すでに人数が MAX_MEMBERS を超えているとき失敗 if(n_members >= MAX_MEMBERS) { cerr << "too many members" << endl; return -1; } // ++演算子の使い方に注意:評価してからインクリメント members[n_members++] = _member; return 0; }

97 98 99 100

3

int Lab::remove_member(LabMember* _member) { int index = MemberIndex(_member); // _member が見つからなかった if(index < 0) return -1; int i; n_members--; // index 以降のポインタを 1 つずつずらす


for(i=index; i<n_members; i++) members[i] = members[i+1]; // 最後を NULL にしておく members[n_members] = 0; // OB の数が MAX_OLD_MEMBERS を超えているときは失敗 if(n_old_members >= MAX_OLD_MEMBERS) { delete _member; return -1; } // OB に追加 old_members[n_old_members++] = _member; return 0;

101 102 103 104 105 106 107 108 109 110 111 112 113 114

116 117 118 119 120 121

123 124 125 126 127 128 129 130

133 134 135 136 137

139

141 142 143 144 145 146 147 148 149 150 151

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

186 187 188

192

195 196 197 198

ostream& operator << (ostream& ost, const Staff& _staff) { ost << "\t" << _staff.name << "\t(title = " << _staff.title << ")" << flush; return ost; }

207

LabMember::˜LabMember() { } // LabMember のコンストラクタも呼び出す->name が初期化される Staff::Staff() : LabMember() { title = OTHER_TITLE; }

199 200 201 202 203 204

Staff::Staff(const string& _name, Title _title) : LabMember(_name) { title = _title; }

205 206

208

Staff::˜Staff() { }

209 210 211 212 213 214 215

Student::Student() : LabMember() { grade = OTHER_GRADE; advisor = 0; thesis_title = ""; }

216 217 218 219 220 221 222 223

Student::Student(const string& _name, Grade _grade) : LabMember(_name) { grade = _grade; advisor = 0; thesis_title = ""; }

224 225 226 227

ostream& operator << (ostream& ost, const Lab& lab) { ost << lab.name << " [" << endl; int i; if(lab.n_members > 0) ost << " current members" << endl; for(i=0; i<lab.n_members; i++) { if(lab.members[i]->GetMemberType() == LabMember::STAFF) ost << *(Staff*)lab.members[i] << endl; else if(lab.members[i]->GetMemberType() == LabMember::STUDENT) ost << *(Student*)lab.members[i] << endl; else ost << *lab.members[i] << endl; } if(lab.n_old_members > 0) ost << " old members" << endl; for(i=0; i<lab.n_old_members; i++) { ost << *lab.old_members[i] << endl; }

// ": name(_name)" は_name を引数として name(string 型) の // コンストラクタを同時に呼び出すことを示す LabMember::LabMember(const string& _name) : name(_name) { }

193 194

ostream& operator << (ostream& ost, const LabMember& _member) { ost << "\t" << _member.name << flush; if(_member.description != "") ost << "\t(" << _member.description << ")" << flush; return ost; }

152 153

185

190

ostream& operator << (ostream& ost, const Student& _student) { ost << "\t" << _student.name << "\t(grade = " << _student.grade << flush; if(_student.advisor) ost << ", advisor = " << _student.advisor->GetName() << ", thesis title = " << _student.thesis_title << ")" << flush; else ost << ")" << flush; return ost; }

LabMember::LabMember() { name = ""; }

183 184

191

138

140

181 182

bool LabMember::operator == (LabMember& _member) { if(name == _member.name) return true; return false; }

131 132

179 180

189

122

}

178

}

115

ost << "]" << flush;

176 177

Student::˜Student() { }

228 229 230 231 232 233 234 235 236 237 238 239

Lab::Lab() { int i; name = ""; for(i=0; i<MAX_MEMBERS; i++) members[i] = 0; for(i=0; i<MAX_OLD_MEMBERS; i++) old_members[i] = 0; n_members = 0; n_old_members = 0; }

240 241 242 243 244 245 246 247 248 249 250

4

Lab::Lab(const string& _name) : name(_name) { int i; for(i=0; i<MAX_MEMBERS; i++) members[i] = 0; for(i=0; i<MAX_OLD_MEMBERS; i++) old_members[i] = 0; n_members = 0; n_old_members = 0; }


ABC Lab

251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

// new で確保したメモリは必ず解放する // members, old_members は配列として宣言したので // 自動的に解放される Lab::˜Lab() { int i; for(i=0; i<MAX_MEMBERS; i++) { if(members[i]) delete members[i]; } for(i=0; i<MAX_OLD_MEMBERS; i++) { if(old_members[i]) delete old_members[i]; } }

members old_members

James

Nancy

Robert

David

Jessica

図 1: main.cc 22 行目までのコードで生成されるデータ

ABC Lab members old_members

3 main.cpp 1 2 3 4

James

/* * main.cc: sample main file * Create: Katsu Yamane, 03.07.16 */

Nancy

Robert

David

Jessica

Cindy

図 2: main.cc 25–30 行目のコードで生成されるデータ

5 6

#include "lab.h"

11

7

12

8

int main(int argc, char** argv) 13 { 14 // 名前を指定して Lab オブジェクトを作成 15 Lab lab("ABC Lab"); 16 // メンバを追加 17 Staff* james = lab.AddStaff("James", 18 Staff::PROFESSOR); 19 Staff* nancy = lab.AddStaff("Nancy", 20 Staff::ASSOCIATE_PROFESSOR); 21 Staff* robert = lab.AddStaff("Robert", Staff::RESEARCH_ASSISTANT); Student* david = lab.AddStudent("David", Student::D1); Student* jessica = lab.AddStudent("Jessica", Student::M1); // 指導教官と論文題目を設定 david->SetAdvisor((LabMember*)james); david->SetThesisTitle("Research on XYZ"); jessica->SetAdvisor((LabMember*)nancy); jessica->SetThesisTitle("Development of PQR"); cout << lab << endl; // 1 年後 lab.RemoveMember("James"); nancy->SetTitle(Staff::PROFESSOR); david->SetGrade(Student::D2); david->SetAdvisor((LabMember*)nancy); jessica->SetGrade(Student::M2); LabMember* cindy = lab.AddMember("Cindy"); cindy->SetDescription("Visiting Professor"); cout << lab << endl; return 0; }

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

sample.exe: main.obj lab.obj g++ $(LINK_FLAGS) -osample.exe main.obj lab.obj main.obj: lab.h main.cc g++ -c $(CXX_FLAGS) -omain.obj main.cc lab.obj: lab.h lab.cc g++ -c $(CXX_FLAGS) -olab.obj lab.cc clean: rm *.obj *.exe

5

実行結果

ABC Lab [ current members James

(title = 0)

Nancy

(title = 1)

Robert

(title = 3)

David

(grade = 3, advisor = James, thesis title = Research on XYZ)

Jessica (grade = 1, advisor = Nancy, thesis title = Development of PQR) ] ABC Lab [ current members Nancy

(title = 0)

Robert

(title = 3)

David

(grade = 4, advisor = Nancy, thesis title = Research on XYZ)

1 2 3 4

4 Makefile

Jessica (grade = 2, advisor = Nancy,

# # sample Makefile # Create: Katsu Yamane, 03.07.16 #

Cindy

thesis title = Development of PQR) old members James ]

5 6

all: sample.exe

7 8 9

(Visiting Professor)

CXX_FLAGS = -O2 LINK_FLAGS =

10

5


C++プログラミング資料