情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引

第Ⅲ部

第10章 クラス

    10.1 クラス
    1. 10.1.1 クラスの宣言
      1. (プログラム例 10.1 ) クラス宣言
    2. 10.1.2 前送りのクラス宣言
    10.2 メンバー関数とフレンド関数
    1. (プログラム例 10.2 ) メンバー関数とフレンド関数
    10.3 コンストラクタとデストラクタ
    1. (プログラム例 10.3 ) 時間データ
    2. (プログラム例 10.4 ) プラントモデル
    3. (プログラム例 10.5 ) ベクトルの内積と絶対値
    4. (プログラム例 10.6 ) リスト構造
    5. (プログラム例 10.7 ) 様々なデータ型の引き渡し
    演習問題10

10.1 クラス

10.1.1 クラスの宣言

  大きなプログラムの場合,その部分的な機能の修正のため,他の機能に対応する部分も理解し,かつ,場合によってはそれらの一部も修正するなど,プログラム全体にわたって理解し,かつ,修正をしなければならないとしたら,大変なことになります.そこで,プログラムのモジュール化が非常に重要になります.各機能毎にモジュール化し,

  1. そのモジュール内部における詳細な処理を知らなくても,適当なインターフェースを介してデータを渡し,また,インターフェースを介して希望する結果が得られる.

  2. モジュールとのやりとりは,インターフェースを介してのみ可能であり,モジュールの外部から,そのモジュール内の処理を直接コントロールすることはできないし,また,コントロールする必要がない.

ようにしておけば,各モジュールは他のモジュールの影響を受けにくくなり,対応する機能の修正はそのモジュールの修正だけで済みます.ある意味では,各モジュールをブラックボックス化するわけです.

  このモジュール化の一つの実現方法が関数です.引数を付けて関数を呼ぶというインターフェースによって,関数内部の処理の詳細を知らなくても結果を得ることができます.また,ローカル変数の存在により,関数外部から関数内部の処理を直接コントロールすることは基本的に不可能です.

  このように,関数もモジュール化の強力な手段ですが,それは,アルゴリズムに重点を置いたブラックボックス化です.しかし,場合によっては,データに重点を置いたブラックボックス化が必要になる場合があります.それが,まさに,C++ のクラスです.クラスでは,データとそれを扱う関数を一つにまとめ,特定のインターフェース(クラス内で宣言された関数)を介してのみ,その内部にアクセスできるようにしています.このような処理をデータの抽象化と呼び,抽象データ型の変数(つまり,あるクラス型として宣言された変数)をオブジェクト(データとそれを操作する手続き-関数-をひとまとめにしたもの,object )と呼びます.

  また,先に説明しましたように,クラスとは,ある「もの」に対し,その「もの」に共通する特徴等をもとにして,形式的な定義を与えたものであるといえます.もう少しプログラミング的な感覚でいえば,「もの」に共通するデータと,それらのデータを処理する方法を記述した手続きの集まりです.例えば,「車」というクラスを定義したとすれば,車の構造を記述するデータと車を動かしたりするのに必要な手続きからなっているはずです.また,クラスは「もの」に対する抽象的な定義ですが,そのインスタンス(オブジェクトとなる)はクラスを具体化したものに相当します.例えば,「車」クラスの場合であれば,そのインスタンスは,ある特定の人が所有する特定の車になります.

  同じプログラムであっても,アルゴリズムに重点を置いたモジュール化を行うのか,または,データに重点を置いたモジュール化を行うのかによって,書き方はかなり異なってきます.問題によって,より適した方法があるはずです.以下の節で述べるクラスに関する詳細説明やプログラム例を見ながら検討してみて下さい.

  クラスは,その定義の方法から見て,ユーザーが新たに定義する変数の型と考えても良いと思います.新しい変数の型を定義すれば,その型の操作方法も必要になります.例えば,実部と虚部という 2 つのデータからなる複素数に対応する型をクラスによって定義したとします.すると,単純な加算ですら,既存の方法,つまり,int 型や double 型と同じような方法を使用することができません.従って,定義した変数の加算をどのようにして実行するかについても定義してやる必要が出てきます.そこで,クラスの定義には,単にデータだけでなく,そのデータを取り扱う方法を記述した関数が必要になってくるわけです.

  クラスclass )は,C++ の最も重要な概念です.基本的には構造体の拡張であり,キーワードの違いを除けば,その宣言方法も構造体と同じです.一般的な表現方法をすると,例えばクラス Example ( Example: クラス識別子)を,

class Example {
		int x;                // プライベートメンバー変数
		 ・・・
	protected:
		int y;                // 派生クラスだけが使用可能なメンバー変数
		 ・・・
	public:
		int z;                // パブリックメンバー変数
		 ・・・
	private:
		double fun1(int);     // プライベートメンバー関数
		 ・・・
	protected:
		double fun2(int);     // 派生クラスだけが使用可能なメンバー関数
		 ・・・
	public:
		double fun3(int);     // パブリックメンバー関数
		 ・・・
	friend double fun4(int); // フレンド関数
	 ・・・
	friend class Example2;   // フレンドクラス
	 ・・・
};
		

のように宣言します.ただし,上の例における public,private 等に対応するすべてのメンバーmember )を所有する必要はありません.public 等を記述しないと,クラス宣言のメンバーがプライベートとみなされる点以外,記述する順番も一般的には決まっていません.なお,クラス識別子のあとに記述される項目(派生クラスの宣言)も存在しますが,それらについては後ほど説明します.

  private の後に記述されたメンバーは,定義されたクラスのメンバー関数,フレンド関数,および,フレンドクラスだけから参照可能です.また,protected の後に記述されたメンバーは,それに加え,派生クラスからも参照可能になります.ただし,多少複雑になりますので,詳細については 13 章を参照してください.public の後に記述されたメンバーは,どこからでも参照可能です.C/C++ の通常の関数や別のクラスに対して,メンバー関数と同様のアクセス権を与えようとしたものが,フレンド関数やフレンドクラスの宣言です.なお,メンバー関数の本体は,クラス定義の中に記述することも,クラス定義の本体では関数の宣言だけを行いクラス宣言の外側(別のファイルでも構わない)に記述することも可能です.

  上の定義からも明らかなように,構造体との大きな違いは,メンバーとして関数を持てることです.さらに,構造体ではそのメンバーをどの関数からでもアクセスできましたが,クラスでは,メンバーを private,public,及び,protected で分類し,各メンバーをアクセスできる範囲を限定しています.C++ は,構造体を,パブリックメンバー変数だけを持つ特殊なクラスと見なしていますので,後に述べるコンストラクタ,派生クラス等,クラスに特有な機能を構造体に対してもそのまま適用できます.

  上のようにして宣言されたクラスのオブジェクトを生成(クラス Example のインスタンスを生成,Example 型の変数の定義)し,そのメンバー変数やメンバー関数を参照するには,構造体と同様,基本的に以下のようにして行います.
Example ex;   // Example 型オブジェクトの生成
EXample *p_ex = new Example;   // p_ex は Example 型オブジェクトへのポインタ
ex.x = 20;   // メンバー変数 x の参照
y    = p_ex->func(10);   // ポインターによるメンバー関数 func の参照
Example ex1 = ex;   // ex の内容がコピーされて ex1 に代入
EXample *p_ex1 = p_ex;   // p_ex1 は p_ex と同じオブジェクトを指す		

  「 7.2.1 型宣言」において,static 指定について述べましたが,メンバー変数やメンバー関数に対して指定した場合は,多少異なった意味を持ちます.以下に示すのは,メンバー変数やメンバー関数に対して static 指定をした場合に対するプログラム例です.
01	#include <stdio.h>
02	
03	class Example {
04			static int s1;
05		public:
06			static int s2;
07			static void out() { printf("from out s1 %d s2 %d\n", s1, s2); }
08	};
09	
10	int Example::s1 = 10;   // static 指定のため必要
11	int Example::s2 = 20;   // static 指定のため必要
12	
13	int main()
14	{
15		printf("from main s2 %d\n", Example::s2);   // s1 は参照不可
16		Example::out();
17		Example t;
18		printf("from main s2 %d\n", t.s2);   // s1 は参照不可
19		t.out();
20		return 0;
21	}
		
  04,06 行目のメンバー変数 s1,s2 に対しては,static 指定が行われています.クラス内の変数に対して static 指定を行うと,静的メンバー変数となります.通常,インスタンスを生成すると,クラス内の変数はインスタンス毎にその変数を記憶するための領域が確保されますが,静的メンバー変数の場合は,インスタンスを生成する前から,その変数を記憶する領域がクラスとは別に確保されます.従って,クラス外のグローバル領域において,変数を宣言し,その領域を確保しておく必要があります( 10,11 行目).また,関数に対して static 指定を行うと,静的メンバー関数となりますが,その関数から参照できる変数は静的メンバー変数だけとなります.

  静的メンバー変数や静的メンバー関数は,インスタンスを生成する前でも,後でも,
クラス名::静的メンバー変数名
クラス名::静的メンバー関数名			
のような形で,変数の参照や関数の実行が可能であり( 15,16 行目),生成した後は,通常のメンバー変数やメンバー関数と同じ参照方法も可能になります( 18,19 行目).また,インスタンスと関係なくその領域が確保されているため,クラス及びそのクラスを継承したすべてのクラスのインスタンスにおいて,その値は同じになります.当然,いずれかにおいてその値を変更すれば,関連する他のクラス及びそのインスタンスにおいて値が変化します.

(プログラム例 10.1 ) クラス宣言 ( A ~ F の部分を可能な限り一つの変数,定数,演算子等で,埋めてください.その際,文字を削除してから,正しい答えを半角文字で,かつ,余分なスペースを入れないで入力してください.)

  クラスの宣言とメンバー変数やメンバー関数の参照方法に関する例です.

(プログラム)
/****************************/
/* クラスの宣言             */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
using namespace std;
/***********************/
/* クラスExampleの宣言 */
/***********************/
class Example {

		int x;   // private メンバー変数

	public:

		int y;   // public メンバー変数

		void v_set1()   // public メンバー関数
		{
			x = 10;
			y = 20;
		}

		void v_set2()   // public メンバー関数
		{
			x = 30;
			y = 40;
		}

		void output()   // public メンバー関数
		{
			cout << "   x = " << x << ",  y = " << y << endl;
		}
};   // 「;」が必要なことに注意

/*************/
/* main 関数 */
/*************/
int main()
{
	Example t1;   // Example 型オブジェクト
	Example *t2 = new Example;   // Example 型オブジェクトへのポインタ

	t1.v_set1();   // メンバー関数の呼び出し
	t2->v_set2();   // メンバー関数の呼び出し

	cout << "オブジェクト t1\n";
	cout << "   y = " << t1.y << endl;   // 変数xへのアクセスはできない
	t1.output();
	cout << "オブジェクト t2\n";
	cout << "   y = " << t2->y << endl;   // 変数xへのアクセスはできない
	t2->output();

	return 0;
}
			

  このプログラムを実行すると,以下に示すような結果が得られます.
オブジェクト t1
   y = 20
   x = 10,  y = 20
オブジェクト t2
   y = 40
   x = 30,  y = 40		

10.1.2 前送りのクラス宣言

  あるクラス C1 で別のクラス C2 を参照し,かつ,クラス C2 側でもクラス C1 を参照しているように,クラスどうしが相互参照するような場合は,どちらを先に宣言してもエラーになります.この場合,次のように前送りのクラス宣言forward class declaration ),または,不完全なクラス宣言imcomplete class declaration )をする事によって解決できます.
class C2;
class C1 {C2 *p; ・・・};
class C2 {C1 *q; ・・・};		
ただし,各クラスで他のクラスを参照できるのは,ポインタと参照の定義に限られます.

10.2 メンバー関数とフレンド関数

  クラスのメンバーとして宣言された関数をメンバー関数member function )と言います.メンバー関数は,その関数が定義されたクラス型の変数(オブジェクト)のプライベートメンバーにアクセスでき,そのオブジェクトと外部とのインターフェースの役割を果たします.下に,メンバー関数の定義の方法を示します.なお,以下においては,クラス名を Example として説明を行っていきます.
関数の型 クラス名::関数名 (引数のリスト)
{
	・・・・・
}		
  上の方法は,クラス宣言の外側で定義する方法ですが,クラスの宣言の中に,関数の宣言だけでなくその本体も記述することができます.その場合,その関数はインライン関数とみなされます.

  クラス宣言の中で,
friend int func(Example &,・・・);		
のように friend を付加して宣言された関数をフレンド関数friend function )と言います.フレンド関数は,メンバー関数のように,オブジェクトのプライベートメンバーにアクセス可能な点を除けば,普通の関数と同じです.また,他のクラス(例えば,Example1 )のメンバー関数を,
friend int Example1::func(Example &,・・・);		
のように宣言し,フレンド関数とすることも可能です.さらに,
friend class List;		
のように宣言し,クラス List のすべてをフレンドとし,そのクラス内のすべてのメンバー関数等からプライベートメンバーを参照できるようにすることも可能です.

  メンバー関数とフレンド関数の違いは,次のプログラム例に見るように,関数の呼び出しやメンバーに対するアクセス方法の違いにあります.メンバー関数の場合はメンバー名だけでアクセスできますが,フレンド関数の場合は,クラス名が必要になります.

  一般に,メンバー関数はそのクラスのメンバー変数にアクセスでき,参照することもその値を変更することも可能です.しかし,下に示す例の 12 行目のように,関数定義の後ろに const を付加すると,そのクラスのメンバー変数を変更できなくなります.従って,12 行目のコメントを外し,11 行目をコメントにすると,14 行目において変数 ct の値を変更しているため,コンパイルエラーになってしまいます.
01	#include <iostream>
02	
03	using namespace std;
04	
05	class Test {
06			int x  = 10;
07			int y  = 20;
08			int ct = 0;
09		public:
10			Test() {}   // コンストラクタ1
11			void print()
12	//		void print() const   // メンバー変数を変更できない
13			{
14				ct++;
15				cout << "x " << x << " y " << y << " ct " << ct << endl;
16			}
17	};
18	
19	int main()
20	{
21		Test t;
22		t.print();
23		t.print();
24	
25		return 0;
26	}
		

(プログラム例 10.2 ) メンバー関数とフレンド関数 ( A ~ D の部分を可能な限り一つの変数,定数,演算子等で,埋めてください.その際,文字を削除してから,正しい答えを半角文字で,かつ,余分なスペースを入れないで入力してください.)

  次のプログラムは,メンバー関数,フレンド関数,フレンドクラスの違いを示したものです.関数の呼び出し方,及び,メンバーの参照方法に注意して下さい.また,fun2 は普通の関数ですので,クラス Example のパブリック変数 y にアクセスすることは可能ですが,プライベート変数 x を参照しようとするとコンパイルエラーになります.

  なお,メンバー関数の中で使用されている this というキーワードは,メンバー関数の中だけで使用でき,起動されたオブジェクト(この場合は,data )へのポインタを表しています.従って,「 this->y 」と「 y 」は同じ意味になります.このように,メンバー関数の中ではメンバー名でメンバーを直接アクセスできますので必要ありませんが,例えば,

   return *this;

のように,自分自身への参照を返したいとき等に使用されます.

  関数 fun1,fun2,fun3 に対して,Example 型オブジェクトを参照で渡している点に注意してください.この例の場合,関数内で,メンバー変数の値を変えているわけではないので,

   void fun1(Example data)

のような一般的な引数の渡し方で正常に動作します.しかし,その場合は,関数の説明の時に述べましたように,オブジェクトのコピーが渡されます.単純な変数の場合は特に問題ありませんが,大きなオブジェクトの場合は,そのコピーを生成するために余分な時間とメモリを必要とします.このコピー生成の無駄を省くために参照渡しを行っています.もちろん,アドレス(ポインタ)で渡しても構いませんが,オブジェクト内の変数を参照するときに,ピリオドではなく,「->」を使用する必要があり,多少プログラムが読みにくくなります.

(プログラム)
/******************************/
/* メンバー関数とフレンド関数 */
/*      coded by Y.Suganuma   */
/******************************/
#include <iostream>
using namespace std;
/***********************/
/* クラスExampleの定義 */
/***********************/
class Example {
		int x;
	public:
		int y;
		void set(int, int);
	friend void fun1(Example &);
};

void fun2(Example &);

/************/
/* main関数 */
/************/
int main()
{
	Example data;       // dataがExample型のオブジェクトであることを宣言

	data.set(10, 20);   // メンバー関数の呼び出し.ピリオドに注意
	fun1(data);         // フレンド関数の呼び出し
	fun2(data);         // 普通の関数の呼び出し

	return 0;
}

/*********************************************/
/* メンバー関数                              */
/*      プログラム例10.1のようにクラス定義の */
/*      内部に記述することもできる           */
/*********************************************/
void Example::set(int a, int b)
{
	x = a;
	this->y = b;        // y = b と同じ
	cout << "x " << x << " y " << y << endl;
}

/****************/
/* フレンド関数 */
/****************/
void fun1(Example &data)
{
	cout << "x " << data.x << " y " << data.y << endl;
}

/**************/
/* 普通の関数 */
/**************/
void fun2(Example &data)
{
	cout << "x ??" << " y " << data.y << endl;   // xにはアクセスできない
}
			

このプログラムを実行すると,以下に示すような結果が得られます.
メンバー関数 x 10 y 20
フレンド関数 x 10 y 20
通常の関数 x ?? y 20
フレンドクラス x 10 y 20		

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

  コンストラクタ構築子constructor )は,オブジェクトを初期化する目的で作成するクラスと同じ名前を持った特別なメンバー関数です.コンストラクタを持つクラスのインスタンスを生成すると,コンストラクタが自動的に呼び出され,オブジェクトを初期化します.初期化は,代入文やメンバー初期設定リストを使用して行われます.

  コンストラクタは関数ですから,通常,引数を持っています.従って,インスタンスの生成の際に,必ず,引数も与えてやる必要があります.ただし,コンストラクタが定義されていない場合や引数のないコンストラクタが定義されている場合は別です(今まで述べてきた例は,これに相当します).

  プログラム例 10.1 においては,Example 型オブジェクト t1,および,*t2 を定義した後,
t1.v_set1();
t2->v_set2();		
とうい方法で,メンバー関数 v_set1,および,v_set2 を呼び出し,その値を設定していました.しかし,関数 v_set1,および,v_set2 の代わりに,
Example(int x1, int y1)
{
	x = x1;
	y = y1;
}
	// 以下のように,メンバー初期設定リストを使用しても良い
Example(int x1, int y1) : x(x1), y(y1) {}		
のようなコンストラクタを用意しておけば,
Example t1(10, 20);   // Example 型オブジェクト
Example *t2 = new Example (30, 40);   // Example 型オブジェクトへのポインタ		
という記述だけで,オブジェクトの定義と初期設定が済んでしまいます.

  また,C++11 においては,initializer_list クラスを利用することによって,ユーザが定義したクラスのオブジェクトに対しても,{1, 2, 3} のように波括弧を使用した初期化子リストによって初期設定を行うことができます.さらに,コンストラクタの呼び出しを,波カッコ { }で記述する一様初期化と呼ぶ機能も存在します.詳細については,以下に示すプログラムとその説明を参考にしてください.
001	#include <iostream>
002	#include <vector>
003	#include <initializer_list>
004	
005	using namespace std;
006	
007	class Test {
008		public:
009			int x = 1000;   // 非静的メンバ変数の初期化(C++11)
010			int y[3];
011			vector<int> v1;
012			vector<int> v2;
013				// コンストラクタ1(メンバー初期設定リストを利用)
014			Test(int x1, int y1[], initializer_list<int> in1, initializer_list<int> in2) :
015	//		explicit Test(int x1, int y1[], initializer_list<int> in1, initializer_list<int> in2) :
016			      x(x1), y(), v1(in1.begin(), in1.end()), v2(in2.begin(), in2.end()) {
017				for (int i1 = 0; i1 < 3; i1++)
018					y[i1] = y1[i1];
019				cout << "(コンストラクタ 1)\n";
020			}
021				// コンストラクタ2(メンバー初期設定リストを利用しない)
022			Test(int x1, int y1[], vector<int> vv1, vector<int> vv2) {
023	//		explicit Test(int x1, int y1[], vector<int> vv1, vector<int> vv2) {
024				x = x1;   // この文がなければ,x の値は 1000 のまま
025				for (int i1 = 0; i1 < 3; i1++)
026					y[i1] = y1[i1];
027				for (const auto& e : vv1)
028					v1.push_back(e);
029				for (const auto& e : vv2)
030					v2.push_back(e);
031				cout << "(コンストラクタ 2)\n";
032			}
033	};
034	
035	void func(int x[]) {
036		cout << "  function";
037		for (int i1 = 0; i1 < 3; i1++)
038			cout << "  " << x[i1];
039		cout << endl;
040	}
041	
042	void func(vector<int> x) {
043		cout << "  function";
044		for (auto e : x)
045			cout << "  " << e;
046		cout << endl;
047	}
048	
049	int main()
050	{
051				// 配列
052		int x[] {1, 2, 3};
053	//	int x[] = {1, 2, 3};   // ok
054		cout << "** 配列 x **\n";
055		for (auto e : x)
056			cout << "  " << e;
057		cout << endl;
058		func(x);
059	//	func({1, 2, 3});  // error
060	
061		int *y = new int [3] {1, 2, 3};
062		cout << "** 配列 y (new) **\n";
063		for (int i1 = 0; i1 < 3; i1++)
064			cout << "  " << y[i1];
065		cout << endl;
066				// vector
067		vector<int> v1 {1, 2, 3};
068	//	vector<int> v1 = {1, 2, 3};   // ok
069		cout << "** vector v1 **\n";
070		for (auto e : v1)
071			cout << "  " << e;
072		cout << endl;
073		func(v1);
074	//	func({1, 2, 3});   // ok
075	
076		vector<int> *v2 = new vector<int> {1, 2, 3};
077		cout << "** vector v2 **\n";
078		for (int i1 = 0; i1 < 3; i1++)
079			cout << "  " << (*v2)[i1];
080		cout << endl;
081				// クラス Test のオブジェクト
082		int ty[] = {4, 5, 6};
083	
084		cout << "** class Test ** ";
085		Test t1 {100, ty, {10, 20, 30}, {1, 2, 3}};   // コンストラクタ1,及び,2 で ok
086	//	Test t1 = {100, ty, {10, 20, 30}, {1, 2, 3}};   // explicit を付けた場合は不可,以下の記述であれば良い
			                                            //     Test t1(100, ty, {10, 20, 30}, {1, 2, 3});
087		cout << "  x:  " << t1.x << "  y:";
088		for (auto e : t1.y)
089			cout << "  " << e;
090		cout << "  v1:";
091		for (auto e : t1.v1)
092			cout << "  " << e;
093		cout << "  v2:";
094		for (auto e : t1.v2)
095			cout << "  " << e;
096		cout << endl;
097	
098		cout << "** class Test ** ";
099		vector<int> tv1 = {10, 20, 30};
100		vector<int> tv2 = {1, 2, 3};
101		Test t2 {100, ty, tv1, tv2};   // コンストラクタ1 では不可,コンストラクタ2 で ok
102	//	Test t2 = {100, ty, tv1, tv2};   // explicit を付けた場合は不可,以下の記述であれば良い
			                             //     Test t1(100, ty, tv1, tv2);
103		cout << "  x:  " << t2.x << "  y:";
104		for (auto e : t2.y)
105			cout << "  " << e;
106		cout << "  v1:";
107		for (auto e : t2.v1)
108			cout << "  " << e;
109		cout << "  v2:";
110		for (auto e : t2.v2)
111			cout << "  " << e;
112		cout << endl;
113	
114		return 0;
115	}
		
052 ~ 065 行目

  配列に対して初期化子リストを使用した例です.下に示す出力結果からも明らかなように,初期化子リストによる初期設定が正しく実行されています.ただし,059 行目のように初期化子リストを関数の引数として使用することはできません.
   ** 配列 x **
     1  2  3
     function  1  2  3
   ** 配列 y (new) **
     1  2  3			
067 ~ 080 行目

  vector クラスのオブジェクトに対して初期化子リストを使用した例です.下に示す出力結果からも明らかなように,初期化子リストによる初期設定が正しく実行されています.また,074 行目のように初期化子リストを関数の引数として使用することもできます.
   ** vector v1 **
     1  2  3
     function  1  2  3
   ** vector v2 **
     1  2  3			
084 ~ 096 行目

  085 行目において,007 ~ 033 行目に定義されているクラス Test のオブジェクトを生成しています.下に示す結果からも明らかなように,初期化子リストが正しく処理されています.この実行結果では,014 ~ 020 行目のコンストラクタが使用されていますが,022 ~ 032 行目のコンストラクタを使用しても正しく動作します.ただし,015 行目や 023 行目のように,explicit という記述を付加すると,086 行目のような記述(代入文を使用した記述)がエラーになってしまいます.なお,explicit 関数指定子は,望ましくない暗黙的型変換を制御するための記述であり,クラス宣言内のコンストラクターの宣言にだけ使用されます.
   ** class Test ** (コンストラクタ 1)
     x:  100  y:  4  5  6  v1:  10  20  30  v2:  1  2  3			
098 ~ 112 行目

  この例では,022 ~ 032 行目のコンストラクタを使用して正しく動作していますが,014 行目の表現に対しては引数を 085 行目のような形で記述する必要があるため,014 ~ 020 行目のコンストラクタだけが存在する場合は,コンパイルエラーになってしまいます.
   ** class Test ** (コンストラクタ 2)
     x:  1000  y:  4  5  6  v1:  10  20  30  v2:  1  2  3
			

  デストラクタ消滅子destructor )は,オブジェクトを消滅させる目的で作成する特別なメンバー関数であり,「~クラス名」という関数名に決まっています.デストラクタに引数を渡したり,デストラクタから値を返したり,また,デストラクタの多重定義をするなどのことはできません.大きさの決まっていないデータを扱うためには,オブジェクト内で new 演算子によりメモリを確保する方法が一般的です.このメモリの確保をコンストラクタの中で行うと,プログラムが非常に書きやすくなります.さらに,確保したメモリは,必要が無くなれば解放してやらなければなりませんが,この処理もデストラクタを使用すれば自動的に行ってくれます.デストラクタに対する具体的な使用方法に関しては,プログラム例 10.5 を参照してください.なお,コンストラクタやデストラクタは,通常,public より後ろで宣言します.

(プログラム例 10.3 ) 時間データ ( A ~ C の部分を可能な限り一つの変数,定数,演算子等で,埋めてください.その際,文字を削除してから,正しい答えを半角文字で,かつ,余分なスペースを入れないで入力してください.)

(プログラム)
/****************************/
/* 時間データの処理         */
/*      coded by Y.Suganuma */
/****************************/
#include <stdio.h>

/********************/
/* クラスTimeの宣言 */
/********************/
class Time {
		int hour, min, sec;
	public:
					// コンストラクタ,2つの引数はデフォルト
		Time(int h, int m = 0, int s = 0)
		{
			hour = h;
			min  = m;
			sec  = s;
		}
					// 以下のように,メンバー初期設定リストを使用しても良い
//		Time(int h, int m = 0, int s = 0) : hour(h), min(m), sec(s) {}
					// コンストラクタ.引数無しも許可.このように引数の異なる
					// 宣言を許す場合は,デフォルト引数又は関数名のオーバーロードが必要
		Time() {}
					// 出力
		void print()
		{
			printf("%2d:%2d:%2d\n", hour, min, sec);
		}
};

/************/
/* main関数 */
/************/
int main()
{
	Time time1(10, 20, 23);      // 10:20:23
	Time time2 = Time(12, 30);   // 12:30:00
	Time time3;                  // 初期設定されない(内容は不定)

	time1.print();
	time2.print();
	time3.print();

	return 0;
}
			

  このプログラムを実行すると,以下に示すような結果が得られます.
10:20:23
12:30: 0
6749984:1970558921:6750032		

(プログラム例 10.4 ) プラントモデル

  例えば,化学プラントでは,パイプを通して原料が供給され,それが反応炉等に入り,その成分構成が変化して次の反応炉等へ進むということが繰り返されます.反応炉内の処理やパイプ内の成分構成は変化しても,各反応炉等はパイプを通して入力が入り,パイプに出力されるという点ではほとんど同じです.そこで,様々な反応炉等に対応するクラスを用意しておき,その内部処理をコンストラクタや関数で行う(この例の場合は,繰り返しがないため,すべてコンストラクタで行っている)ようにしておけば,それらを適当に組み合わせることによってプラントを実現できるはずです.

  ここでは,
feed  : 原料の供給
add  : 2 つの原料を混ぜる
product: 反応を行う		
の 3 つのクラスを用意し,次のようなプラント(?)をシミュレーションするプログラムを書いてみました.

/****************************/
/* プラントモデル           */
/*      coded by Y.Suganuma */
/****************************/
#include <stdio.h>

/********************/
/* クラスFeedの定義 */
/********************/
class Feed {         /* 原料の供給 */
	public:
		double x, y, z;
						  // コンストラクタ
		Feed(double a, double b, double c)
		{
			x = a;
			y = b;
			z = c;
		}
						  // 出力
		void print()
		{
			printf("x %f y %f z %f (feed)\n", x, y, z);
		}
};

/*******************/
/* クラスAddの定義 */
/*******************/
class Add {		  /* 混合 */
	public:
		double x, y, z;
						  // コンストラクタ
		Add(Feed &a, Feed &b)
		{
			x = a.x + b.x;
			y = a.y + b.y;
			z = a.z + b.z;
		}
						  // 出力
		void print()
		{
			printf("x %f y %f z %f (add)\n", x, y, z);
		}
};

/***********************/
/* クラスProductの定義 */
/***********************/
class Product {	  /* 製品 */
	public:
		double x, y, z;
						  // コンストラクタ
		Product(Add &a)
		{
			x = 0.1 * a.x;
			y = 0.2 * a.y;
			z = 0.9 * a.x + 0.8 * a.y;
		}
						  // 出力
		void print()
		{
			printf("x %f y %f z %f (product)\n", x, y, z);
		}
};

/************/
/* main関数 */
/************/
int main()
{
	Feed feed1(10.0, 20.0, 0.0);
	feed1.print();

	Feed feed2(5.0, 10.0, 0.0);
	feed2.print();

	Add add1(feed1, feed2);
	add1.print();

	Product pro1(add1);
	pro1.print();

	return 0;
}
		
(出力)
x 10.000000 y 20.000000 z 0.000000 (feed)
x 5.000000 y 10.000000 z 0.000000 (feed)
x 15.000000 y 30.000000 z 0.000000 (add)
x 1.500000 y 6.000000 z 37.500000 (product)			
  このプログラムの実行により,以下のような結果が得られます.A ~ L の空白部分を埋めてください(小数点以下 1 桁).その際,文字を削除してから,正しい答えを半角文字で,かつ,余分なスペースを入れないで入力してください.

(プログラム例 10.5 ) ベクトルの内積と絶対値 ( A ~ D の部分を可能な限り一つの変数,定数,演算子等で,埋めてください.その際,文字を削除してから,正しい答えを半角文字で,かつ,余分なスペースを入れないで入力してください.)

  次は,n 次元ベクトルの内積と絶対値を計算するプログラムです.任意の次元に対応するため,new 演算子を使用しています.このような場合,new 演算子で確保した領域を開放するためにデストラクタが必要になります.

(プログラム)
/****************************/
/* ベクトルの内積と絶対値   */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
#include <math.h>
using namespace std;
/**********************/
/* Vectorクラスの定義 */
/**********************/
class Vector {
	public:
		int n;
		double *v;
		Vector (int n1)   // コンストラクタ
		{
			n = n1;
			v = new double [n];
		}
		~Vector()   // デストラクタ
		{
			if (n > 0)
				delete [] v;
		}
		double zettai();   // 絶対値
		double naiseki(Vector &);   // 内積
		void input();   // 要素の入力
};

/**********/
/* 絶対値 */
/**********/
double Vector::zettai()
{
	double x = 0.0;
	for (int i1 = 0; i1 < n; i1++)
		x += v[i1] * v[i1];
	return sqrt(x);
}

/*********************/
/* 内積              */
/*      b : ベクトル */
/*********************/
double Vector::naiseki(Vector &b)
{
	double x = 0.0;
	for (int i1 = 0; i1 < n; i1++)
		x += v[i1] * b.v[i1];
	return x;
}

/********/
/* 入力 */
/********/
void Vector::input()
{
	for (int i1 = 0; i1 < n; i1++) {
		cout << "   " << i1+1 << "番目の要素は? ";
		cin >> v[i1];
	}
}

/******************/
/* mainプログラム */
/******************/
int main()
{
	Vector a(2), b(2);

	cout << "a\n";
	a.input();
	cout << "b\n";
	b.input();

	cout << "内積 " << a.naiseki(b) << endl;
	cout << "絶対値 " << a.zettai() << " " << b.zettai() << endl;

	return 0;
}
			

  このプログラムを実行すると,以下に示すような結果が得られます.
a
   1番目の要素は? 1
   2番目の要素は? 2
b
   1番目の要素は? 2
   2番目の要素は? 5
内積 12
絶対値 2.23607 5.38516		
  単に,内積や絶対値を求めることが目的であれば,以下に示すように,C++ の標準ライブラリ内にある inner_product を使用すれば簡単に求めることができます.
#include <iostream>
#include <vector>
#include <numeric>
#include <cmath>

using namespace std;

int main()
{
			// 初期設定
	vector<double> v1 {1, 2, 3};
	cout << "v1 :";
	for (auto x : v1)
		cout << "  " << x;
	cout << endl;

	vector<double> v2 {2, 3, 4};
	cout << "v2 :";
	for (auto x : v2)
		cout << "  " << x;
	cout << endl;

	double s1 = inner_product(v1.begin(), v1.end(), v2.begin(), 0);
	double s2 = sqrt(inner_product(v1.begin(), v1.end(), v1.begin(), 0));
	cout << "  v1 と v2 の内積 = " << s1 << ", 絶対値(v1) = " << s2 << endl;

	return 0;
}
		

(プログラム例 10.6 ) リスト構造

  プログラム例 8.5 をクラスを用いて書いた例です.

/****************************/
/* リスト構造               */
/*      coded by Y.Suganuma */
/****************************/
#include <stdio.h>
#include <string.h>

/********************/
/* クラスListの定義 */
/********************/
class List
{
		char *st;
		List *next;

	public :
				// コンストラクタ
		List () { next = NULL; }

		List (char *s)
		{
			next = NULL;
			st   = new char [strlen(s)+1];
			strcpy(st, s);
		}

		void add(List *);   // データの追加
		void del(char *);   // データの削除
		void output();   // 出力
};

/**************************************/
/* データの追加                       */
/*      dt : Listクラスのオブジェクト */
/**************************************/
void List::add(List *dt)
{
	List *lt2 = this;
	int sw = 1;

	while (sw > 0) {
					// 最後に追加
		if (lt2->next == NULL) {
			lt2->next = dt;
			sw        = 0;
		}
					// 比較し,途中に追加
		else {
			List *lt1 = lt2;
			lt2       = lt2->next;
			int k     = strcmp(dt->st, lt2->st);   // 比較
			if (k < 0) {                     // 追加
				dt->next  = lt2;
				lt1->next = dt;
				sw        = 0;
			}
		}
	}
}

/*********************/
/* データの削除      */
/*      st1 : 文字列 */
/*********************/
void List::del(char *st1)
{
	List *lt2 = this;
	int sw = 1;

	while (sw > 0) {
					// データが存在しない場合
		if (lt2->next == NULL) {
			printf("      指定されたデータがありません!\n");
			sw = 0;
		}
					// 比較し,削除
		else {
			List *lt1 = lt2;
			lt2       = lt2->next;
			int k     = strcmp(st1, lt2->st);   // 比較
			if (k == 0) {                 // 削除
				lt1->next = lt2->next;
				sw        = 0;
			}
		}
	}
}

/**********************/
/* リストデータの出力 */
/**********************/
void List::output()
{
	List *nt = this->next;

	while (nt != NULL) {
		printf("   data = %s\n", nt->st);
		nt = nt->next;
	}
}

/****************/
/* main program */
/****************/
int main ()
{
	int sw = 1;
	char st[100];
	List base, *lt;

	while (sw > 0) {
		printf("1:追加,2:削除,3:出力,0:終了? ");
		scanf("%d", &sw);
		switch (sw) {
			case 1:   // 追加
				printf("   データを入力してください ");
				scanf("%s", st);
				lt = new List(st);
				base.add(lt);
				break;
			case 2:   // 削除
				printf("   データを入力してください ");
				scanf("%s", st);
				base.del(st);
				break;
			case 3:   // 出力
				base.output();
				break;
			default :
				sw = 0;
				break;
		}
	}

	return 0;
}
		
  このプログラムを実行すると,以下に示すような結果が得られます.
1:追加,2:削除,3:出力,0:終了? 1
   データを入力してください xyz
1:追加,2:削除,3:出力,0:終了? 1
   データを入力してください sdd
1:追加,2:削除,3:出力,0:終了? 1
   データを入力してください abc
1:追加,2:削除,3:出力,0:終了? 2
   データを入力してください sdd
1:追加,2:削除,3:出力,0:終了? 3
   data = abc
   data = xyz
1:追加,2:削除,3:出力,0:終了? 0
		

  先にも示しましたように,以下に示すのは,コンテナ内の set クラスを使用して記述した場合の例です.

/****************************/
/* リスト処理               */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
#include <set>
#include <string>

using namespace std;

/**********************/
/* リストデータの出力 */
/*      lt : リスト   */
/**********************/
void output(set<string> &s) {
	set<string>::iterator it;
	cout << "要素数: " << s.size() << "\n";
	for (it = s.begin(); it != s.end(); it++)
		cout << "  " << *it;
	cout << "\n";
}

/****************/
/* main program */
/****************/
int main ()
{
	int sw = 1;
	string str;
	set <string> s;

	while (sw > 0) {
		cout << "1:追加,2:削除,3:出力,0:終了? ";
		cin >> sw;
		switch (sw) {
			case 1:   // 追加
				cout << "   データを入力してください ";
				cin >> str;
				s.insert(str);
				break;
			case 2:   // 削除
				cout << "   データを入力してください ";
				cin >> str;
				s.erase(str);
				break;
			case 3:   // 出力
				output(s);
				break;
			default :
				sw = 0;
				break;
		}
	}

	return 0;
}
		

(プログラム例 10.7 ) 様々なデータ型の引き渡し

  この例では,108 行目に見るように,11 種類の方法でデータを関数 method に引数として渡しています.
001	/****************************/
002	/* 様々なデータ型の引き渡し */
003	/*      coded by Y.Suganuma */
004	/****************************/
005	#include <iostream>
006	using namespace std;
007	
008	/***********************/
009	/* クラスComplexの定義 */
010	/***********************/
011	class Complex {
012		public:
013			double re, im;
014			int *a;
015							 // コンストラクタ
016			Complex(double re, double im, int x)
017			{
018				this->re   = re;
019				this->im   = im;
020				this->a    = new int [1];
021				this->a[0] = x;
022			}
023	};
024	
025	/*****************************************************/
026	/* 関数の例                                          */
027	/*      a, b, c : int 型                             */
028	/*      ar_11, ar_12 : int 型 1 次元配列             */
029	/*      ar_21, ar_22, ar_23 : int 型 2 次元配列      */
030	/*      cx1, cx2, cx3 : Complex クラスのオブジェクト */
031	/*****************************************************/
032	void method(int a, int *b, int &c, int ar_11[], int *ar_12, int ar_21[][3], int **ar_22, 
	    int *ar_23, Complex cx1, Complex *cx2, Complex &cx3)
033	{
034		a  = 9;
035		*b = 9;
036		c  = 9;
037		ar_11[0] = 99;
038		ar_12[0] = 99;   // *ar_12 = 99;
039		ar_21[1][0] = 999;
040		ar_22[1][0] = 999;
041		ar_23[3] = 999;
042		cx1.im   = 9999;
043		cx1.a[0] = 555;
044		cx2->im  = 9999;
045		cx2->a[0] = 555;
046		cx3.im   = 9999;
047		cx3.a[0] = 555;
048	}
049	
050	/*************/
051	/* main 関数 */
052	/*************/
053	int main()
054	{
055		int a = 1, b = 1, c = 1;
056		int ar_11 [] = {10, 20};
057		int *ar_12 = new int [2];
058		ar_12[0] = 10;
059		ar_12[1] = 20;
060		int ar_21 [][3] = {{100, 200, 300}, {400, 500, 600}};
061		int **ar_22 = new int * [2];
062		for (int i1 = 0; i1 < 2; i1++) {
063			ar_22[i1] = new int [3];
064			for (int i2 = 0; i2 < 3; i2++) {
065				if (i1 == 0)
066					ar_22[i1][i2] = 100 * (i2 + 1);
067				else
068					ar_22[i1][i2] = 100 * (i2 + 4);
069			}
070		}
071		int ar_23 [][3] = {{100, 200, 300}, {400, 500, 600}};
072		Complex cx1(1000, 2000, 3000);
073		Complex cx2(1000, 2000, 3000);
074		Complex cx3(1000, 2000, 3000);
075						// 関数をcall前
076		cout << "   ***関数をcall前***\n";
077		cout << "a = " << a << " b = " << b << " c = " << c << endl;
078		cout << "ar_11[0] = " << ar_11[0] << ", ar_11[1] = " << ar_11[1] << endl;
079		cout << "ar_12[0] = " << ar_12[0] << ", ar_12[1] = " << ar_12[1] << endl;
080		for (int i1 = 0; i1 < 2; i1++) {
081			for (int i2 = 0; i2 < 3; i2++) {
082				if (i2  < 2)
083					cout << "ar_21[" << i1 << "][" << i2 << "] = " << ar_21[i1][i2] << ", ";
084				else
085					cout << "ar_21[" << i1 << "][" << i2 << "] = " << ar_21[i1][i2] << endl;
086			}
087		}
088		for (int i1 = 0; i1 < 2; i1++) {
089			for (int i2 = 0; i2 < 3; i2++) {
090				if (i2  < 2)
091					cout << "ar_22[" << i1 << "][" << i2 << "] = " << ar_22[i1][i2] << ", ";
092				else
093					cout << "ar_22[" << i1 << "][" << i2 << "] = " << ar_22[i1][i2] << endl;
094			}
095		}
096		for (int i1 = 0; i1 < 2; i1++) {
097			for (int i2 = 0; i2 < 3; i2++) {
098				if (i2  < 2)
099					cout << "ar_23[" << i1 << "][" << i2 << "] = " << ar_23[i1][i2] << ", ";
100				else
101					cout << "ar_23[" << i1 << "][" << i2 << "] = " << ar_23[i1][i2] << endl;
102			}
103		}
104		cout << "cx1.re = " << cx1.re << ", cx1.im = " << cx1.im << ", cx1.a[0] = " << cx1.a[0] << endl;
105		cout << "cx2.re = " << cx2.re << ", cx2.im = " << cx2.im << ", cx2.a[0] = " << cx2.a[0] << endl;
106		cout << "cx3.re = " << cx3.re << ", cx3.im = " << cx3.im << ", cx3.a[0] = " << cx3.a[0] << endl;
107						// 関数をcall
108		method(a, &b, c, ar_11, ar_12, ar_21, ar_22, &ar_23[0][0], cx1, &cx2, cx3);
109						// 関数をcall後
110		cout << "   ***関数をcall後***\n";
111		cout << "a = " << a << " b = " << b << " c = " << c << endl;
112		cout << "ar_11[0] = " << ar_11[0] << ", ar_11[1] = " << ar_11[1] << endl;
113		cout << "ar_12[0] = " << ar_12[0] << ", ar_12[1] = " << ar_12[1] << endl;
114		for (int i1 = 0; i1 < 2; i1++) {
115			for (int i2 = 0; i2 < 3; i2++) {
116				if (i2  < 2)
117					cout << "ar_21[" << i1 << "][" << i2 << "] = " << ar_21[i1][i2] << ", ";
118				else
119					cout << "ar_21[" << i1 << "][" << i2 << "] = " << ar_21[i1][i2] << endl;
120			}
121		}
122		for (int i1 = 0; i1 < 2; i1++) {
123			for (int i2 = 0; i2 < 3; i2++) {
124				if (i2  < 2)
125					cout << "ar_22[" << i1 << "][" << i2 << "] = " << ar_22[i1][i2] << ", ";
126				else
127					cout << "ar_22[" << i1 << "][" << i2 << "] = " << ar_22[i1][i2] << endl;
128			}
129		}
130		for (int i1 = 0; i1 < 2; i1++) {
131			for (int i2 = 0; i2 < 3; i2++) {
132				if (i2  < 2)
133					cout << "ar_23[" << i1 << "][" << i2 << "] = " << ar_23[i1][i2] << ", ";
134				else
135					cout << "ar_23[" << i1 << "][" << i2 << "] = " << ar_23[i1][i2] << endl;
136			}
137		}
138		cout << "cx1.re = " << cx1.re << ", cx1.im = " << cx1.im << ", cx1.a[0] = " << cx1.a[0] << endl;
139		cout << "cx2.re = " << cx2.re << ", cx2.im = " << cx2.im << ", cx2.a[0] = " << cx2.a[0] << endl;
140		cout << "cx3.re = " << cx3.re << ", cx3.im = " << cx3.im << ", cx3.a[0] = " << cx3.a[0] << endl;
141		return 0;
142	}
		
1 番目の引数 a

  055 行目で設定された a の値が,032 行目の a にコピーされ,関数 method に渡されます.034 行目において a の値を変更しても,コピーが変更されただけですから,main プログラム内の a の値はそのままです(出力結果の 015 行目参照).

2 番目の引数 &b

  変数 b のアドレスのコピーが関数 method に渡されます.アドレスが指す場所は main プログラムの b そのものですから,035 行目の実行によって,main プログラムにおける b の値も変更されます(出力結果の 015 行目参照).

3 番目の引数 c

  108 行目だけを見る限り,変数 a と同じ渡し方に見えますが,032 行目を見て下さい.このような渡し方を参照渡しと呼びます.036 行目のように,アドレス渡しの場合と変数の参照方法は異なりますが,実際的な効果はアドレスを渡す場合と同様です(出力結果の 015 行目参照).なお,032 行目において,int &c の代わりに const int &c と記述しておけば,値の変更は不可能になります.

4 番目の引数 ar_11

  1 次元配列を渡しています.C/C++ に対する配列とポインタにおいて説明しましたように,配列は,ポインタが記憶領域の先頭アドレスを指しているとみなすことが出来ます.この場合も,記憶領域を指すアドレスが関数 method に渡されます.従って,関数内で記憶領域の値を変更すれば,main プログラム内の配列の値も変化します(出力結果の 016 行目参照).なお,1 次元配列の場合は,032 行目において,int ar_11[] の代わりに int *ar_11 と記述しても構いません.

5 番目の引数 ar_12

  new 演算子で生成した 1 次元配列を渡しています.基本的に,通常の 1 次元配列と同じです(出力結果の 017 行目参照).

6 番目の引数 ar_21

  2 次元配列を渡しています.1 次元配列の場合と同様,記憶領域の先頭を指すアドレスを渡していることになりますが,032 行目の記述に注意して下さい.このプログラムの場合,int ar_21[2][3] と記述すべきところを,行の大きさ 2 を省略し,int ar_21[][3] と記述しています.3 次元以上の配列の場合も同様ですが,省略可能なのは最も左側に相当する要素数だけです.この場合も,当然,main プログラムにおける配列の値が変化しています(出力結果の 018,019 行目参照).

7 番目の引数 ar_22

  new 演算子で生成した 2 次元配列を渡しています.基本的に,通常の 2 次元配列と同じです(出力結果の 020,021 行目参照).ただし,032 行目の記述に注意して下さい.

8 番目の引数 &ar_23[0][0]

  C/C++ に対する配列とポインタにおいて説明しましたように,多次元配列の各要素は連続した領域に確保されますので,それを 1 次元配列として扱うことが可能です.この例では,2 次元配列の先頭のアドレスを渡し,関数側では 1 次元配列として処理しています(041 行目,出力結果の 022,023 行目参照).

9 番目の引数 cx1

  Complex クラスのオブジェクトを渡しています.オブジェクト全体がコピーされ関数に渡されます.従って,関数内でオブジェクト内の値を変更しても,main プログラム内のオブジェクトはその影響を受けませんが,a[0] に関しては,関数内での変更がそのまま反映されています(出力結果の 024 行目参照).これは,オブジェクト全体がコピーされ関数に渡されても,new 演算子を使用している部分は,配列 a のアドレスだけがコピーされ,そのアドレスが指す配列内のデータはコピーされないため,main プログラム内の cx1 と 関数内の cx1 は同じデータを指すことになるからです.

10 番目の引数 &cx2

  Complex クラスのオブジェクトのアドレスを渡しています.従って,関数内で値を変更すると,main プログラム内のオブジェクトの値も変更されます(出力結果の 025 行目参照).

11 番目の引数 cx3

  Complex クラスのオブジェクトに対する参照渡しです.9 番目の引数のような渡し方をすると,オブジェクトが大きい場合,コピーを作成するために多くの時間や領域が必要になります.10 番目の引数のように,アドレスを渡せばその問題は解決しますが,044,045 行目のように,変数の参照方法を変えなくてはなりません.従って,オブジェクトを引数とする場合は,参照渡しが多く使用されます.勿論,期待した結果も得られますし(出力結果の 026 行目参照),値を変更しない場は,3 番目の引数の項で説明したような const を使用すれば可能です.ただし,new 演算子を使用した配列 a に関しては,const 指定が有効に働きません.
  このプログラムによって,以下に示すような結果が得られます(行番号は説明用に追加).
01	   ***関数をcall前***
02	a = 1 b = 1 c = 1
03	ar_11[0] = 10, ar_11[1] = 20
04	ar_12[0] = 10, ar_12[1] = 20
05	ar_21[0][0] = 100, ar_21[0][1] = 200, ar_21[0][2] = 300
06	ar_21[1][0] = 400, ar_21[1][1] = 500, ar_21[1][2] = 600
07	ar_22[0][0] = 100, ar_22[0][1] = 200, ar_22[0][2] = 300
08	ar_22[1][0] = 400, ar_22[1][1] = 500, ar_22[1][2] = 600
09	ar_23[0][0] = 100, ar_23[0][1] = 200, ar_23[0][2] = 300
10	ar_23[1][0] = 400, ar_23[1][1] = 500, ar_23[1][2] = 600
11	cx1.re = 1000, cx1.im = 2000, cx1.a[0] = 3000
12	cx2.re = 1000, cx2.im = 2000, cx2.a[0] = 3000
13	cx3.re = 1000, cx3.im = 2000, cx3.a[0] = 3000
14	   ***関数をcall後***
15	a = 1 b = 9 c = 9
16	ar_11[0] = 99, ar_11[1] = 20
17	ar_12[0] = 99, ar_12[1] = 20
18	ar_21[0][0] = 100, ar_21[0][1] = 200, ar_21[0][2] = 300
19	ar_21[1][0] = 999, ar_21[1][1] = 500, ar_21[1][2] = 600
20	ar_22[0][0] = 100, ar_22[0][1] = 200, ar_22[0][2] = 300
21	ar_22[1][0] = 999, ar_22[1][1] = 500, ar_22[1][2] = 600
22	ar_23[0][0] = 100, ar_23[0][1] = 200, ar_23[0][2] = 300
23	ar_23[1][0] = 999, ar_23[1][1] = 500, ar_23[1][2] = 600
24	cx1.re = 1000, cx1.im = 2000, cx1.a[0] = 555
25	cx2.re = 1000, cx2.im = 9999, cx2.a[0] = 555
26	cx3.re = 1000, cx3.im = 9999, cx3.a[0] = 555
		

演習問題10

[問1]名前( char ),給与( int ),及び,年齢( int )をクラスで表現し,そこへ入出力を行うプログラムを書け.

[問2] n (入力)個の 3 次元空間の点の座標を入力した後,原点からの平均距離を計算し,平均距離以上離れた点の個数を出力するプログラムを,クラスを使用して書け.

[問3]名前,住所,電話番号をクラスを使用して記述し,名前を入力すると電話番号を出力するプログラムを書け.

[問4]最大待ち行列長を 10 として,待ち行列をクラスで表現するプログラムを書け.ただし,待ち行列は,配列に待ち行列に入っている要素-数字-を格納することによって表すものとする.メンバー関数としては,待ち行列に要素を入れる関数 put(int num),待ち行列から要素を取り出す関数 get(),及び,待ち行列の状態を出力する関数 qprint() を用意せよ.

[問5]レコードの格納と取り出しを文字列キーで行うハッシュ表のクラスを書け.その際,レコードの挿入,探索,及び,削除を行うメンバー関数も用意せよ.

[問6]英語の単語を入力し,それを 2 進木として記憶するプログラムをクラスを利用して書け.ただし,新しい単語が入力されたとき,記憶されている単語よりアルファベット順で前にあるなら左,そうでなければ右の枝をたどるものとする.例えば,11 個の単語が,「 network parameters are optimized from empirical data and the optimal number 」という順に入力された場合は以下のようになる.

情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引