【C++言語】第7回「参照」new,delete,値渡し,ポインタ渡し,参照渡し

本アーティクルは「応用プログラミングA」の2013年5月28日の講義内容の補足です。
内容の正確さは保証しません。
これをそのままコピーしてレポートを書く人がいますが、全く勉強にならないのでやめましょう。

 

問題1「newとdelete」

前回、「C++でのポインタと配列の利用」ではクラスのオブジェクトをいきなり配列にとって、そのポインタを挿げ替えるような初期化を行っていたわけですが、ふつうは(少なくとも最近の言語は)こんな”野蛮な”オブジェクトの生成をしたりはしません。
(もちろんmallocやfreeなどもありますが…)

今回はnew演算子とdelete演算子を使った例を見てみましょう。

// 2013/5/28 Q1
/* new と delete 演算子
 * 三角形を表す Triangle クラスのオブジェクトを、new 演算子を使って作成する。
 * オブジェクトには初期値として、(3,4,5)を与える。
 * 各辺の長さと長さと面積を出力した後に delete でメモリを開放する。
 * なお、 delete 演算子によってデストラクタが呼び出されることを確認するために、
 * デストラクタを追加している。
*/
#include <iostream>
#include <cmath>
using namespace std;
class Triangle {
	double a, b, c; //辺の長さ
public:
	//コンストラクタ
	Triangle(double u, double v, double w) {
		a = u; b = v; c = w;
	}
	//デストラクタ
	~Triangle() {
		cout << "デストラクタが呼び出されました\n";
	}
	void set_a(double x) { a = x; }
	void set_b(double x) { b = x; }
	void set_c(double x) { c = x; }
	double get_a() {return a;}
	double get_b() {return b;}
	double get_c() {return c;}
	double area(); //面積を求める関数
	void show();  //結果を表示する
};
double Triangle::area() {
	double s;
	s = (a+b+c)/2.0f;
	return sqrt(s*(s-a)*(s-b)*(s-c));
}
void Triangle::show() {
	cout << "辺a = " << a << "\n";
	cout << "辺b = " << b << "\n";
	cout << "辺c = " << c << "\n";
	cout << "面積 = " << area() << "\n"; 
} 

int main() { 
	Triangle *tri; 
	tri = new Triangle(3,4,5); 	
/* new演算子はコンストラクタを指定して新しいオブジェクトの実体のポインタを取得する */ 
/* 1行で書くとこんな感じ 
	Triangle *tri = new Triangle(3,4,5); 	*/
	tri->show();
	delete tri;
	return 0;
}

new演算子を使うとメモリの動的確保が行えます。deleteはnewで確保したメモリを開放します。
newとdeleteは演算子でmalloc()やfree()は関数なので書式が異なります。
このサンプルでは

Triangle *tri;
tri = new Triangle(3,4,5);

という書式で、コンストラクタを呼んでクラスのインスタンスを取得しています。

なおnew/deleteを使った配列の場合も覚えておきましょう。たとえばint型10個分の配列を動的確保する場合

int *p;
p = new int[10];
for (int i =0; i<10; i++ ) {
    p[i] = i; 
} 
delete [] p;  //このカッコの付き方に注意 

new演算子を使ってクラスのインスタンスを取得した場合、そのメンバ変数やメンバ関数にアクセスするときはアロー演算子(->)を使用します。

tri->show();

 

問題2「値渡し、ポインタ渡し、参照渡し」

 
C++で関数にオブジェクトを渡す際に、「値渡し」、「ポインタ渡し」、「参照渡し(reference)」といった方法でオブジェクトを渡すことができます。問題2ではこれらを実験するプログラムを作成します。

// 2013/5/28 Q2
/* 値渡し、ポインタ渡し、参照渡し
*/
#include <iostream>
using namespace std;

class Coord {
	double x,y,z;
public:
	void set(double u,double v, double w) {
		x = u; y = v; z = w;
	}
	void show();
};
void Coord::show() {
	cout << "(" << x << "," << y << "," << z << ")";
}
//以下の関数はクラス内のメソッドではなく、オブジェクトの外でやり取りするオペレータ関数
//値渡し
void setCoord1(Coord ob) {
	double u,v,w;
	cout << "数字を3つ入力してください:";
	cin >> u >> v >> w;
	ob.set(u,v,w); //実際にはここで値は設定できないことに注意!
}  //ここでobのスコープが終了するため、設定した値は消滅する。
//ここで生成されたオブジェクトの実体obはobjとは別のメモリ空間にいることに注意

//ポインタ渡し
void setCoord2(Coord *ob) {
  //渡すときは"&obj"、受けるときは"*ob"と書く!
	double u,v,w;
	cout << "数字を3つ入力してください:";
	cin >> u >> v >> w;
	ob->set(u,v,w);  //アロー演算子"->"をつかってメソッドset()を呼ぶ 
}  //ここでobのスコープが終了するため、設定した値は消滅する。
// obは消滅するが、実際のメモリ上のポインタが渡されているため、
// 処理と処理結果はobjに残る

//参照渡し
void setCoord3(Coord &ob) {
  //参照渡しで受け取るときは"&ob"になる。なぜならポインタだから。
	double u,v,w;
	cout << "数字を3つ入力してください:";
	cin >> u >> v >> w;
	ob.set(u,v,w);  //オブジェクトそのものがきているので、"."ドット演算子になる 
}  
//ここでobのスコープが終了するが....
//ポインタ渡しと同様、処理結果はobjに残る

int main() {
	Coord obj;
	obj.set(1.1, 2.3, 5.5);
	cout << "オブジェクトの初期値:";	obj.show(); cout << "\n\n";

	cout << "値渡しで関数にオブジェクトを渡します\n";
	setCoord1(obj);
	cout << "オブジェクトの値:";	obj.show(); cout << "\n\n";

	cout << "ポインタ渡しで関数にオブジェクトを渡します\n";
	setCoord2(&obj);  //アンパサンドを使う scanf(&buf)と同じ! 
                              //関数の入力ではなく受け取り容器
	cout << "オブジェクトの値:";	obj.show(); cout << "\n\n";

	cout << "参照渡しで関数にオブジェクトを渡します\n";
	setCoord3(obj);  //&がつかないので値渡しにそっくりだが、
                             //関数の実装での受け方が違います
	cout << "オブジェクトの値:";	obj.show(); cout << "\n\n";

	return 0;
}

チャック・ノリス問題として出題してみたり。

わかりやすい解説
www.aerith.net/design/argument-j.html

問題3「参照渡しを用いた値の交換」トランプ問題

★レポート締切前なので未完です。

// 2013/5/28 Q3
/* トランプ問題
*/
#include
#include
using namespace std;

enum suit { SPADE, HEART, CLUB, DIAMOND } ;
class PCard {
//from the question
};
//参照渡し仮引数
void swap(PCard &ob1, PCard &ob2) {
	PCard ob;
	ob = ob1;	ob1 = ob2; ob2 = ob;
}

int main() {
	PCard deck[52];
	for ( int i=0; i< 4; i++ ){ //suit
		for (int j =1; j<= 13; j++ ){  //number
			switch(i) {
			case 0:
				deck[i*13+j-1].set(SPADE, j);
				break;
				//★以下すべてのsuitに実施(未実装)
			}
		}
	}
	//カードの初期化終了
	//内容一覧
	for (int i=0; i<52; i++) {
		deck[i].show();
	}

	//シャッフル
	for (int i =0; i<100; i++) {
		int a,b;
		a = rand() % 52;
		b = rand() % 52;
		swap(deck[a], deck[b]);
		//ここにAとBを入れ替えました、的な表示を入れておくとわかりやすい
	}
	//再度内容一覧
	for (int i=0; i<52; i++) {
		deck[i].show();
	}

	return 0;
}

【C++】第6回:C++でのポインタと配列の利用

応用プログラミングA(C++言語)、前回の「C++のフレンド関数」に引き続き、今週は「C++でのポインタと配列の利用」です。

C++言語はオブジェクト指向(クラス指向)ではありますが、C言語のパワフルなポインタも使えることは意外と知られていません。
(とはいえ実用上はオブジェクトの配列を使うことが多いと思います)

今回は「学生の成績を管理するクラス」を作成して、ここまでの復習を兼ねた総合問題という設計になっています。

出題された課題は3問あります。
Q1、Q2、Q3…と順に解いていかないと、最後までたどり着けませんので注意です。

またサンプル中に出てくる学生の名前は昨年のAKB48からガラッと変わって『ラブライブ!』からの引用が多いようですが、打つのが大変なのと、私自身が思い入れがないので適当なボカロあたりの名前に差し替わっております。
このあたり、まるまるコピーした場合は減点対象となりますので要注意。

まずQ1です。

ここでは学生のデータを管理するStudentListクラスを作成します。

class StudentList {
	char name[50]; //学生の氏名
	int id, grade; //学籍番号、学年
public:
	void set(char *n, int i, int g);
	void show();
};

このオブジェクトの配列に10人分のデータをコンソールから入力し、すべて入力し終わったらコンソールに出力する基本となるプログラムを作ります。

「独習C++(第4版)」の例4.2を参考にするとよいようです。
独習C++ 第4版

//配列とポインタ:クラスオブジェクトを配列でとった場合も、C言語におけるポインタと同様に扱える。
#include <iostream>
#include <cstring>
using namespace std;
class StudentList {
	char name[50];
	int id, grade;
public:
	void set(char *n, int i, int g);
	void show();
};
void StudentList::set(char *n, int i, int g) {
	strcpy(name, n);
	id = i;		grade = g;
}
void StudentList::show() {
	cout << "氏名:" << name;
	cout << "\t学籍番号:" << id;
	cout << "\t学年:" << grade << "\n";
}

int main() {
	StudentList slist[10];		 //入力用にオブジェクトの配列を宣言
	int id, grd;	char nm[50]; //入力用変数
	int max = 3;				 //最大件数
	StudentList *ptr;			 //配列の操作用にポインタ変数を宣言
	ptr = slist; //ポインタ渡し,ここで配列とポインタを接続
	for (int i=0; i<max ; i++ ) {
		cout << i+1 << "人目のデータを入力します\n";
		cout << "名前を入力してください:";  cin >> nm;
		cout << "学籍番号を入力してください:"; cin >> id;
		cout << "学年を入力してください:";  cin >> grd;
		//仮の入力用変数からポインタの先頭にデータを格納し、ポインタを進行させる.
		ptr->set(nm, id, grd); ptr++;
		//※このptr++で配列1行分、ポインタが進む
	}
	ptr = slist;  //ふたたびポインタをリセット
	for (int i = 0; i < max; i++ ) {
		cout << i+1 << "\t";
		ptr->show(); //このような形でメソッドが呼べることに注意
		ptr++;
	}
	return 0;
}

実行すると、10人分の名前と学籍番号、学年の入力を強いられるだけのマゾプログラムです。
しかも最後の return 0; にブレイクポイントを仕込んでおかないと結果が一瞬で流れて消えます。
それではあまりにご無体ですので、ここではmax=3として端折っています。レポートではmax=3としたら誤りです。

ちなみに講義中はptrのことを『ハンドル』と呼んでしまいましたが、厳密には異なります。
ここは固定のメモリアドレスをptrに渡しているので『ポインタ』と呼ぶのが適切でしょう。
『ハンドル』とは、動的にメモリ配置が変わる場合、たとえばガーベージコレクションなどを備えたJavaやC#のような環境において、実メモリアドレスが移動しても、捕まえておけるオブジェクトのことを『ハンドル』と呼ぶべきようです。

つづいてQ2です。タイトルは「総合問題1」となっています。

先ほどのQ1の続きからはじめて、成績を管理するResultListクラスを追加します。
先述のStudentListクラスをPublicに継承して、公開メンバとして100個までの科目名を格納し、その単位異数と評価を格納します。また格納されている科目数を記憶する変数を非公開メンバ変数として持ちます。関数は評価を格納するための関数Regist()と、全科目の科目名、単位数、評価を出力するshowResult()を持ちます。

以下のようなクラス設計、継承(inheritance)の関係がイメージできましたでしょうか?

Classes
(Visual Studio 2010の「クラスダイアグラム」機能で描画してみました)

継承をコードにするとこんな感じになります。

class ResultList : public StudentList {
	char	course[100][50];	//科目名
	int		credit[100];		//単位数
	char	grade[100];			//評価
	int		number;				//格納してある科目数
public:
	ResultList() { number = 0; }
	void regist(char *title, int crd, char grd);
	void showResult();
};

なお、今回のプログラムでは入力チェックなどは一切行いません。削除や変更もしませんし、科目名の重複もチェックしません。

//Q2:総合問題1 成績管理(入力)
#include <iostream>
#include <cstring>
using namespace std;
class StudentList {
	char name[50];
	int id, grade;
public:
	void set(char *n, int i, int g);
	void show();
};
void StudentList::set(char *n, int i, int g) {
	strcpy(name, n);
	id = i;		grade = g;
}
void StudentList::show() {
	cout << "氏名:" << name;
	cout << "\t学籍番号:" << id;
	cout << "\t学年:" << grade << "\n";
}
//ここから上はQ1と同じ
//ここから下はテキストにある通り、StudentListを継承したResultListを宣言
class ResultList : public StudentList {
	char	course[100][50];	//科目名
	int		credit[100];		//単位数
	char	grade[100];			//評価
	int		number;				//格納してある科目数
public:
	ResultList() { number = 0; }
	void regist(char *title, int crd, char grd);
	void showResult();
};
void ResultList::regist(char *title, int crd, char grd) {
	strcpy(course[number], title);
	credit[number] = crd;
	grade[number]  = grd;
	number++;
}
void ResultList::showResult() {
	//格納してある科目数すべてについてリストする
	for (int i = 0; i < number; i++ ) {
		cout << "科目名:" << course[i];
		cout << "\t単位数:" << credit[i];
		cout << "\t評価:" << grade[i] << "\n";
	}
}

int main() {
	ResultList rlist[10];
	int i,n; char str[50]; char c;
	//名前学籍学年データ , 継承したResultListの配列にセットします
	rlist[0].set("高坂穂乃果",1123005, 3);
	rlist[1].set("絢瀬絵里", 1023003, 4);
	rlist[2].set("南ことり", 1123008, 3);
	rlist[3].set("園田海未", 1123039, 3);
	rlist[4].set("初音ミク", 1023010, 3);
	rlist[5].set("ルカ",	 1223007, 2);
	rlist[6].set("リン",	 1023007, 4);
	rlist[7].set("レン",	 1223008, 2);
	rlist[8].set("かいと",   1023015, 4);
	rlist[9].set("らぴす",   1323013, 1);

	while (1) {
		cout << "学生一覧\n";
		for (i = 0; i < 10 ; i++ ) {
			cout << i+1 << "\t";
			rlist[i].show(); //普通はこう呼ぶほうが多いですよね。継承元のStudentList::show()です
		}
		cout << "何番の学生の成績を入れますか?\n";
		cin >> i;
		cout << "成績を入力してください\n入力終了はendを科目名に入れて下さい\n";
		while(1) {
			cout << "科目名:"; cin >> str;
			if (strcmp(str, "end") == 0) break;
			cout << "単位数:"; cin >> n;
			cout << "評価:";   cin >> c;
			rlist[i-1].regist(str, n, c );
		}
		rlist[i-1].show();
		cout << "この学生のデータを入力しました。\n終了するなら1を続けるなら0を:";
		cin >> n;
		if (n == 1) break;
	}
	//結果表示
	for (i = 0; i < 10; i++ ) {  //個々の学生についてループ
		rlist[i].show();		//個々の学生の情報について表示
		rlist[i].showResult();	//個々の科目の成績について表示
		cout << "\n";
	}
	return 0;
}

【実行結果】
cpp0521q2

9番目の「かいと」(KAIT!?)と10番目の「らぴす」の成績を入力しています。
「応用プログラミングA」が同じ名前なのですが、見た目には動作しています、が、デバッガ仕掛けるとわかりますが、別の科目になっています。
重複チェックを入れるのはさほど難しいことではないので考察としてやってみてもよいのでは。
また結果を表示するshow()ももう一工夫してもいいように思います(Q3にもありますので…)。
それに「end」と入れずに[0]を入れると終わるなど、工夫のしようはありそうです。
cinとifを使ったコマンドプロンプトによる対話は、インタラクティブなプログラムの基本中の基本なので しれっと 書けるようになってください。

さて最後のQ3「総合問題2」です。

前述Q2のResultListクラスに総合成績評価をあらわすGPAを追加します。
GPAは成績評価のSを4、Aを3、Bを2、Cを1、それ以外を0とした取得講義の平均で求めます。
また先ほどのRegist()を拡張してすでに同盟の科目の成績が入力されている場合には評価を上書き修正するようにします。
さらに科目と成績を削除するdel()も実装します。

0521q3class

以下、サンプルソースです。
main()がないととっかかりがつかみづらいと思いますので。
★レポート締切前なので、ResultListクラスの個々のメソッド実装は未公開です。

//0521 課題3 総合問題2
#include <iostream>
#include <cstring>
using namespace std;

class StudentList {
	char name[50];
	int id;
	int grade;
public:
	void set(char *n, int i, int g);
	void show();
};
void StudentList::set(char *n, int i, int g) {
	strcpy(name, n);
	id = i;
	grade = g;
}
void StudentList::show()	{
	cout << "氏名:" << name;
	cout << "\t学籍番号:" << id;
	cout << "\t学年:" << grade << "\n";
}
class ResultList : public StudentList {
	char course[100][50];
	int credit[100];
	char grade[100];
	int number;
	double gpa;  //今回追加
public:
	ResultList() { number = 0; gpa = 0; }
	void regist(char *title, int crd, char grd);
	void showResult();
	void del(char *title);
	double get_gpa() { return gpa; }
};

void ResultList::regist(char *title, int crd, char grd) {
	int i, sumc = 0, sumg = 0;
	for (i = 0; i < number; i++)	{
		if (strcmp(course[i], title) == 0) {
			credit[i] = crd;
			grade[i] = grd;
			break;
		}
	}
	if (i == number)	{
		strcpy(course[number], title);
		credit[number] = crd;
		grade[number] = grd;
		number++;
	}
	for (i = 0; i < number; i++) {
		sumc += credit[i];
		switch (grade[i])	{
			case 'S':
				sumg += 4 * credit[i];
				break;
			case 'A':
				sumg += 3 * credit[i];
				break;
			case 'B':
				sumg += 2 * credit[i];
				break;
			case 'C':
				sumg += 1 * credit[i];
				break;
			default:
				break;
		}
	}
	gpa = (double)sumg / sumc;
}

void ResultList::showResult() {
	for (int i = 0; i < number; i++) {
		cout << "科目名:" << course[i];
		cout << "\t単位数:" << credit[i];
		cout << "\t評価:" << grade[i] << "\n";
	}
}

void ResultList::del(char *title) {
	int i, j, sumc = 0, sumg = 0;
	for (i = 0; i < number; i++) {
	   if (strcmp(course[i], title) == 0) {
// 以下は登録されている科目の順番を変えないように処理している.順番を変えてもよいならもっと簡単
		for (j = i; j < number; j++) {
			strcpy(course[j], course[j+1]);
			credit[j] = credit[j+1];
			grade[j] = grade[j+1];
		}
		number--;
		for (j = 0; j < number; j++) {
			sumc += credit[j];
			switch (grade[j]) {
				case 'S':
					sumg += 4 * credit[j];
					break;
				case 'A':
					sumg += 3 * credit[j];
					break;
				case 'B':
					sumg += 2 * credit[j];
					break;
				case 'C':
					sumg += 1 * credit[j];
					break;
				default:
					break;
			}
		}
		gpa = (double)sumg / sumc;
		break;
	   }
	}
}
//以下はmainの参考。
int main() {
	ResultList rlist[10];
	int i, j, n;
	char str[50];
	char c;
	//学生ダミーデータ:レポートでそのまま使用した場合はカンニングとみなします :-p
	rlist[0].set("高坂穂乃果", 1123005, 3);
	rlist[1].set("絢瀬絵里", 1023003, 4);
	rlist[2].set("南ことり", 1123008, 3);
	rlist[3].set("園田海未", 1123039, 3);
	rlist[4].set("星空凛", 1023010, 4);
	rlist[5].set("西木野真姫", 1223014, 2);
	rlist[6].set("東條希", 1023007, 4);
	rlist[7].set("小泉花陽", 1223008, 2);
	rlist[8].set("矢澤にこ", 1023015, 4);
	rlist[9].set("アルパカ", 1323013, 1);
	while (1) {
		cout << "学生一覧\n";
		for (i = 0; i < 10; i++) {
			cout << i+1 << "\t";
			rlist[i].show();
		}
		// 以下では入力値の検証を行っていない.本当は入力値の検証を行った方がよい
		cout << "何番の学生のデータを処理しますか?";
		cin >> i;
		cout << "成績を入力するには1を、削除するには2を入力してください:";
		cin >> j;
		cout << "入力を終了するには科目名にendと入力してください。\n";
		if (j == 1) {
			while (1) {
				cout << "科目名:";	cin >> str;
				if (strcmp(str, "end") == 0) break;
				cout << "単位数:";	cin >> n;
				cout << "評価:";	cin >> c;
				rlist[i-1].regist(str, n, c);
			}
		} else {
			while (1) {
				cout << "科目名:";	cin >> str;
				if (strcmp(str, "end") == 0) break;
				rlist[i-1].del(str);
			}
		}
		rlist[i-1].show();
		cout << "この学生のデータを入力しました。\n\n";
		cout << "入力を終了しますか?\n";
		cout << "終了するなら1を、続けるなら0を入力してください:";
		cin >> n;
		if (n == 1) break;
	}
	for (i = 0; i < 10; i++) {
		rlist[i].show();
		rlist[i].showResult();
		cout << "GPA:" << rlist[i].get_gpa() << "\n\n";
	}
	return 0;
}

実行結果のスクリーンショットはうまく、登録や削除を説明できるように複数撮りましょうね!
たとえば、このスクリーンショットだけでは何が何だかわかりませんよね?

0521q3

第7回に続きます。

C++のフレンド関数

2013/5/14の「応用プログラミングA(C++)」の講義ふりかえり.

Q1は,独習C++の3章までの復習として関数とオブジェクト.
クラスに属していないメイン関数を作って,先に現れているクラスCoordを引数や戻り値に指定している.

#include
#include
using namespace std;
//クラスCoordの宣言
class Coord {
	double x,y,z;
public:
	void set_x(double u) {x=u;}
	void set_y(double v) {y=v;}
	void set_z(double w) {z=w;}
	double get_x() {return x;}
	double get_y() {return y;}
	double get_z() {return z;}
	void Show();
};
void Coord::Show() {
	cout << "(" << x << "," << y << "," << z << ")";
}

//Coordオブジェクトを返す、中点を求める関数
Coord mid_Coord(Coord ob1, Coord ob2);
//Coordオブジェクトを返す、距離を求める関数
double dist_Coord(Coord ob1, Coord ob2);

int main() {
	Coord p1,p2,p3;
	//p1,p2に3次元座標をセットしていく
	p1.set_x(1.0f);		p1.set_y(6.5f);		p1.set_z(-10.0f);
	p2.set_x(-1.0f);	p2.set_y(1.5f);		p2.set_z(4.0f);

	cout << "点A";	p1.Show(); cout << endl;
	cout << "点B";	p2.Show(); cout << endl;
	//中点p3を取得
	p3 = mid_Coord(p1,p2);
	cout <<"ABの中点";	p3.Show();	cout << endl;
	//距離を求める,値を返すだけなのでリダイレクトで出力できる
	cout <<"AB間の距離" << dist_Coord(p1,p2) << endl;
	return 0;
}
//中点を求める関数の実装
Coord mid_Coord(Coord ob1, Coord ob2) {
	Coord ob;
	ob.set_x( (ob1.get_x() + ob2.get_x()) / 2.0f );
	ob.set_y( (ob1.get_y() + ob2.get_y()) / 2.0f );
	ob.set_z( (ob1.get_z() + ob2.get_z()) / 2.0f );
	return ob;
}
//距離を求める関数の実装,1行で実装してみる
double dist_Coord(Coord ob1, Coord ob2) {
	return sqrt(
		( ob2.get_x() - ob1.get_x() ) * ( ob2.get_x() - ob1.get_x() ) +
		( ob2.get_y() - ob1.get_y() ) * ( ob2.get_y() - ob1.get_y() ) +
		( ob2.get_z() - ob1.get_z() ) * ( ob2.get_z() - ob1.get_z() )
		);
}

Q2はQ1の復習に引き続き,「フレンド関数」を使った実装方法を学ぶ.
色名と色の値を扱うクラスCNameを引数として,円クラスCircleのオブジェクトに対して,「色が一致しているか?」を判定するisSame(CName col)を宣言・実装する必要が有るため friend を名乗る必要がある.

#include <iostream>
#include <cstring>
using namespace std;
class CName;//ここはクラス名だけ宣言してみた
class Circle {
	double x,y,radius;	int r,g,b;
public:
	void set_xyr(double s, double t, double r) {	x = s; y = t; radius = r;	}
	void set_color(int u, int v, int w) {			r = u; g = v; b = w;	}
	void Show();
	bool isSame(CName col);
};
class CName {
	char name[50];	int r,g,b;
public:
	CName (char* str, int u, int v, int w);
	char* get_name() {		return name;	}
	void Show();
	friend bool Circle::isSame(CName col);
};
//friend関数isSameの実装
bool Circle::isSame(CName col) {
	if (r == col.r && g == col.g && b == col.b ) {
		return true;
	} else {
		return false;
	}
}
//CName(色名,RGB値)のコンストラクタ
CName::CName(char* str, int u, int v, int w) {
	strcpy(name, str);
	r = u; g = v; b = w;
}
void CName::Show() {
	cout << name << ":(" << r << "," << g << "," << b << ")\n";
}

int main() {
	Circle c;
	CName co1("Red",255,0,0); 	CName co2("Green",0,255,0); 	CName co3("Blue",0,0,255);
	c.set_xyr(2.0f,3.0f,5.0f);
	c.set_color(255,0,0); //赤
	cout << "円の情報を表示\n"; c.Show();
	cout << "色1の情報を表示\n"; co1.Show();
	cout << "色2の情報を表示\n"; co2.Show();
	cout << "色3の情報を表示\n"; co3.Show();
	cout << "この円の色は" << co1.get_name();
	if (c.isSame(co1)) {
		cout << "です" << endl;
	} else {
		cout << "ではありません" << endl;
	}
	cout << "この円の色は" << co2.get_name();
	if (c.isSame(co2)) {
		cout << "です" << endl;
	} else {
		cout << "ではありません" << endl;
	}
	cout << "この円の色は" << co3.get_name();
	if (c.isSame(co3)) {
		cout << "です" << endl;
	} else {
		cout << "ではありません" << endl;
	}

	return 0;
}
//void Show()の実装
void Circle::Show() {
	cout << "座標("<< x << ","<< y <<") 半径=" << radius << endl;
	cout << "RGB=("<< r <<","<< g <<","<< b << ")" << endl;
}

Q3はQ2の配列による拡張.

まだ課題提出締め切り前なので,main文以下だけ掲示しておきます.→全文掲載しました

#include
#include
using namespace std;

class CName;

class Circle {
	double x, y;
	double radius;
	int r, g, b;
public:
	void set_xyr(double s, double t, double r) {
		x = s;
		y = t;
		radius = r;
	}
	void set_color(int u, int v, int w) {
		r = u;
		g = v;
		b = w;
	}
	void show();
	bool isSame(CName col);
};

class CName {
	char name[50];
	int r, g, b;
public:
	CName(char* str, int u, int v, int w);
	char* get_name() {
		return name;
	}
	void show();
	friend bool Circle::isSame(CName col);
};

void Circle::show() {
	cout << "座標:(" << x << "," << y << ")\n";
	cout << "半径:" << radius << "\n";
	cout << "RGB:(" << r << "," << g << "," << b << ")\n";
}

bool Circle::isSame(CName col) {
	if (r == col.r && g == col.g && b == col.b)
		return true;
	else
		return false;
}

CName::CName(char* str, int u, int v, int w) {
	strcpy(name, str);
	r = u;
	g = v;
	b = w;
}

void CName::show() {
	cout << name << ":(" << r << "," << g << "," << b << ")\n";
}

int main() {
	Circle ob[3];
	CName co[8] = { CName("White", 255, 255, 255),
					CName("Black", 0, 0, 0),
					CName("Red", 255, 0, 0),
					CName("Green", 0, 255, 0),
					CName("Blue", 0, 0, 255),
					CName("Cyan", 0, 255, 255),
					CName("Magenta", 255, 0, 255),
					CName("Yellow", 255, 255, 0) };
	int i, j, flag;

	ob[0].set_xyr(2.0, 3.0, 5.0);
	ob[0].set_color(255, 0, 0);
	ob[1].set_xyr(4.0, 5.0, 6.5);
	ob[1].set_color(0, 255, 0);
	ob[2].set_xyr(1.5, 1.5, 2.0);
	ob[2].set_color(150, 150, 150);

	for (i = 0; i < 3; i++) {
		cout << "円" << i+1 << "の情報を表示\n";
		ob[i].show();
	}

	cout << "\n登録してある色一覧\n";
	for (i = 0; i < 8; i++) {
		co[i].show();
	}
	cout << "\n";

	for (i = 0; i < 3; i++){
		flag = 0;
		for (j = 0; j < 5; j++) {
			if (ob[i].isSame(co[j])) {
				cout << "円" << i+1 << "の色は" << co[j].get_name() << "です\n";
				flag = 1;
				break;
			}
		}
		if (!flag) {
			cout << "円" << i+1 << "の色は登録されている色にありません\n";
		}
	}

	return 0;
}

【実行結果】

半径:5
GB:(255,0,0)
円2の情報を表示
座標:(4,5)
半径:6.5
GB:(0,255,0)
円3の情報を表示
座標:(1.5,1.5)
半径:2
GB:(150,150,150)

登録してある色一覧
hite:(255,255,255)
lack:(0,0,0)
ed:(255,0,0)
reen:(0,255,0)
lue:(0,0,255)
yan:(0,255,255)
agenta:(255,0,255)
ellow:(255,255,0)

円1の色はRedです
円2の色はGreenです
円3の色は登録されている色にありません

次回は「C++でのポインタと配列の利用」です。