ページ 11

バグについて

Posted: 2008年5月30日(金) 14:14
by あなざぁ
コンパイルしてエラーが発生したときに、
バグの部分を自分で見つけ出すコツはありますか?<!--2

C++ 象徴クラスの活用法

Posted: 2008年6月14日(土) 06:04
by 鷹見
おはようございます。
C++をひと通り勉強しましたのですが、象徴クラスの利便性がいまいち解りません。
又 
私はクラスのオブジェクトをグローバルに置くことがほとんどです。
グローバルにはあまり置かないほうがよいでしょうか?

なるべく実用的なプログラムの書き方をしたいと思っています。
なにか良い参考例やアドバイスがありましたらお願いします。

Re:C++ 象徴クラスの活用法

Posted: 2008年6月14日(土) 14:37
by たいちう
象徴クラス?抽象クラスですよね。

> なるべく実用的なプログラムの書き方をしたいと思っています。
> なにか良い参考例やアドバイスがありましたらお願いします。

まず、どうやって書くかよりも何を書くかです。
入門書を一冊位終えた頃には、これを考えなくてはなりません。
オブジェクト指向を身に付けてから、書きたいプログラムを書くのではなく、
書きたいプログラムを書く過程で、オブジェクト指向を身に付けましょう。
何のプログラムを書きたいですか?書く必要があるのですか?

オブジェクト指向の良い例とされているのが、「デザインパターン」として
まとまっています。書籍もサイトも色々ありますので、自分に合ったものを
探してみてはいかがでしょうか。

クラスを使わずに、変数も全てグローバルでも、プログラムを作ることはできます。
作りたいプログラムを自由に完成させてから、「デザインパターン」等を
当てはめて改良することで、色々な書き方のメリット・ディメリット、つまり、
変更の容易さや可読性、冗長性、危険性などが理解できると思いますよ。
このような改良は「リファクタリング」といいます。
この方法についても調べてみると良いでしょう。

Re:C++ 象徴クラスの活用法

Posted: 2008年6月14日(土) 14:59
by tk-xleader
>象徴クラスの利便性がいまいち解りません。

抽象クラスの基本的な使い方は、複数のオブジェクトに共通の操作方法を持たせる事です。
例えば、軽自動車と普通自動車の二つのクラスを作るとします。この二つは規格や最高速度、加速性能などは違うので、同じクラスにすると言うのはおかしな話です。
ところが、どちらにしろ自動車である事に間違いはないのですから、同じ「走る」ことや、「止まる」こと、「曲がる」ことが、同じアクセルやブレーキ、ハンドルでできるはずです。そこで、「自動車」の共通の操作方法を持たせるのが自然ですよね。それが、抽象基本クラスということになります。以下参考。
#include<iostream>
#include<string>
#include<sstream>
#include<exception>
#include<new>
#include<cstdlib>
using namespace std;

class iCar{
	protected:
	string mkind;
	int mforward;
	public:
	//進行方向を表す定数
	static const int North;
	static const int East;
	static const int South;
	static const int West;
	
	//コンストラクタ
	iCar(const string& kind){
		mkind=kind;
		mforward=North;//初期進行方向は北方面
	}
	//純粋仮想関数
	virtual void Run()=0;	//進行方向へ加速
	virtual void Stop()=0;	//停止
	virtual void TurnRight()=0;	//右に曲がる
	virtual void TurnLeft()=0;	//左に曲がる
	virtual bool isStop()=0;	//停止しているかのフラグを返す。
	virtual string StateString()=0;	//状況を表す文字列を返す。
	
	//出力関数
	friend ostream& operator<<(ostream&stream,iCar&obj);
};

ostream& operator<<(ostream&stream,iCar&obj){
	char *fwstr[/url]={"北","東","南","西"};
	stream<<"自動車の種類:"<<obj.mkind<<endl;
	stream<<"進行方向:"<<fwstr[obj.mforward]<<endl;
	stream<<obj.StateString();
	return stream;
}

const int iCar::North=0;
const int iCar::East=1;
const int iCar::South=2;
const int iCar::West=3;

class NormalCar:public iCar{
	int speed;
	public:
	NormalCar():iCar("普通自動車"){
		speed=0;
	}
	
	void Run(){
		if(speed<40)speed+=10;
		else if(speed<90)speed+=7;
		else if(speed<175)speed+=3;
		else if(speed<199)speed+=2;
		else if(speed<200)speed=200;
	}
	void Stop(){
		speed=0;
	}
	void TurnRight(){
		mforward=(mforward+1)%4;
		speed/=12;
	}
	void TurnLeft(){
		mforward=(((unsigned)mforward)-1U)&3;
		speed/=10;
	}
	bool isStop(){
		return speed==0;
	}
	string StateString(){
		ostringstream state;
		state<<"速度:"<<speed<<"km/h";
		return state.str();
	}
};

class LightCar:public iCar{
	int speed;
	public:
	LightCar():iCar("軽自動車"){
		speed=0;
	}
	
	void Run(){
		if(speed<30)speed+=8;
		else if(speed<90)speed+=5;
		else if(speed<145)speed+=1;
	}
	void Stop(){
		speed=0;
	}
	void TurnRight(){
		mforward=(mforward+1)%4;
		speed/=8;
	}
	void TurnLeft(){
		mforward=(((unsigned)mforward)-1U)&3;
		speed/=6;
	}
	bool isStop(){
		return speed==0;
	}
	string StateString(){
		ostringstream state;
		state<<"速度:"<<speed<<"km/h";
		state<<"最高速度:"<<145<<"km";
		return state.str();
	}
};

int main()
{
	iCar *car;
	int select;
	cout<<"どちらの車を選びますか?"<<endl;
	cout<<"1:普通乗用車 2:軽自動車"<<endl;
	cin>>select;
	try{
		if(select==1){
			car=new NormalCar;
		}else if(select==2){
			car=new LightCar;
		}else{
			cout<<"正しい選択をしてください"<<endl;
			exit(0);
		}
	}catch(bad_alloc&){
		cerr<<"メモリの確保に失敗しました"<<endl;
		abort();
	}
	
	cin.get();
	
	bool isloop=true;
	while(isloop){
		cout<<"車を走らせますか?(Y/N):";
		if(cin.get()=='Y')car->Run();
		cin.get();
		if(car->isStop()){
			cout<<"車は止まっています。運転を続けますか?(Y/N):"; 
			if(cin.get()=='N')isloop=false;
			cin.get();
		}
		int happening=rand();
		if(happening==0){
			cout<<"子供が飛び出してきました。停止します。"<<endl;
			car->Stop();
		}else if(happening%25==0){
			int select;
			cout<<"交差点です。どうしますか?"<<endl;
			cout<<"1:直進 2:右に曲がる 3:左に曲がる"<<endl;
			cin>>select;
			cin.get();
			if(select==2)car->TurnRight();
			else if(select==3)car->TurnLeft();
		}
		cout<<*car<<endl;;
	}
	
	return 0;
}
たぶんこんな感じです。

Re:C++ 象徴クラスの活用法

Posted: 2008年6月14日(土) 15:13
by tk-xleader
あっ、ミスってました。これが正しいコードです。
#include<iostream>
#include<string>
#include<sstream>
#include<exception>
#include<new>
#include<cstdlib>
using namespace std;

class iCar{
	protected:
	string mkind;
	int mforward;
	public:
	//進行方向を表す定数
	static const int North;
	static const int East;
	static const int South;
	static const int West;
	
	//コンストラクタ
	iCar(const string& kind){
		mkind=kind;
		mforward=North;//初期進行方向は北方面
	}
	//純粋仮想関数
	virtual void Run()=0;	//進行方向へ加速
	virtual void Stop()=0;	//停止
	virtual void TurnRight()=0;	//右に曲がる
	virtual void TurnLeft()=0;	//左に曲がる
	virtual bool isStop()=0;	//停止しているかのフラグを返す。
	virtual string StateString()=0;	//状況を表す文字列を返す。
	
	//出力関数
	friend ostream& operator<<(ostream&stream,iCar&obj);
};

ostream& operator<<(ostream&stream,iCar&obj){
	char *fwstr[/url]={"北","東","南","西"};
	stream<<"自動車の種類:"<<obj.mkind<<endl;
	stream<<"進行方向:"<<fwstr[obj.mforward]<<endl;
	stream<<obj.StateString();
	return stream;
}

const int iCar::North=0;
const int iCar::East=1;
const int iCar::South=2;
const int iCar::West=3;

class NormalCar:public iCar{
	int speed;
	public:
	NormalCar():iCar("普通自動車"){
		speed=0;
	}
	
	void Run(){
		if(speed<40)speed+=10;
		else if(speed<90)speed+=7;
		else if(speed<175)speed+=3;
		else if(speed<199)speed+=2;
		else if(speed<200)speed=200;
	}
	void Stop(){
		speed=0;
	}
	void TurnRight(){
		mforward=(mforward+1)%4;
		speed/=12;
	}
	void TurnLeft(){
		mforward=(((unsigned)mforward)-1U)&3;
		speed/=10;
	}
	bool isStop(){
		return speed==0;
	}
	string StateString(){
		ostringstream state;
		state<<"速度:"<<speed<<"km/h";
		return state.str();
	}
};

class LightCar:public iCar{
	int speed;
	public:
	LightCar():iCar("軽自動車"){
		speed=0;
	}
	
	void Run(){
		if(speed<30)speed+=8;
		else if(speed<90)speed+=5;
		else if(speed<145)speed+=1;
	}
	void Stop(){
		speed=0;
	}
	void TurnRight(){
		mforward=(mforward+1)%4;
		speed/=8;
	}
	void TurnLeft(){
		mforward=(((unsigned)mforward)-1U)&3;
		speed/=6;
	}
	bool isStop(){
		return speed==0;
	}
	string StateString(){
		ostringstream state;
		state<<"速度:"<<speed<<"km/h";
		state<<"最高速度:"<<145<<"km";
		return state.str();
	}
};

int main()
{
	iCar *car;
	int select;
	cout<<"どちらの車を選びますか?"<<endl;
	cout<<"1:普通乗用車 2:軽自動車"<<endl;
	cin>>select;
	try{
		if(select==1){
			car=new NormalCar;
		}else if(select==2){
			car=new LightCar;
		}else{
			cout<<"正しい選択をしてください"<<endl;
			exit(0);
		}
	}catch(bad_alloc&){
		cerr<<"メモリの確保に失敗しました"<<endl;
		abort();
	}
	
	cin.get();
	
	bool isloop=true;
	while(isloop){
		cout<<"車を走らせますか?(Y/N):";
		if(cin.get()=='Y')car->Run();
		cin.get();
		if(car->isStop()){
			cout<<"車は止まっています。運転を続けますか?(Y/N):"; 
			if(cin.get()=='N')isloop=false;
			cin.get();
		}
		int happening=rand();
		if(happening==0){
			cout<<"子供が飛び出してきました。停止します。"<<endl;
			car->Stop();
		}else if(happening%25==0){
			int select;
			cout<<"交差点です。どうしますか?"<<endl;
			cout<<"1:直進 2:右に曲がる 3:左に曲がる"<<endl;
			cin>>select;
			cin.get();
			if(select==2)car->TurnRight();
			else if(select==3)car->TurnLeft();
		}
		cout<<*car<<endl;;
	}
	
	delete car;
	
	return 0;
}
解放忘れという初歩的なミスをしてしまうとは…

Re:C++ 抽象クラスの活用法

Posted: 2008年6月15日(日) 05:05
by 鷹見
(タイトル打ち間違えすみませんでした。)

お二人ともありがとうございます。
一度自由に作ってから、徐々に効率が良くなるように修正していくのもテですね。


抽象クラスはこの先、いくつの派生クラス作るかわからないとき抽象化しておき
派生クラスの数を増やしやすいことでしょうか?

抽象クラスのオブジェクトポインタを一つ作り、必要な物(上のコードではswitchの所)をそのポインタに入れることでプログラムのルート上今回は使わない(例 上コード、軽自動車 又は 普通自動車)物を作らず。
メモリの消費を抑えることになる。

と言うのが考えてみた結果になりました。


コードまで載せて頂き助かります やっぱりnewで確保したのはdeleteするのはぜったいですね。
私はまだあまり慣れていないので結構deleteし忘れます。今回のアドバイスを元にもう少し打ちながら考えてみます、ありがとうございました。
<!--1

K&Rの演習問題

Posted: 2008年6月22日(日) 20:44
by Diaoler
 初の書込みです……ではなくて,昨年「羽流布」という名で書込みました。

 Diaoler(ディアオラ)です。改めて宜しくお願いしますm(_ _)m

 今,K&R(第2版)で勉強しています。
 そこで質問なんですが,

【演習問題1-14】
 入力中の異なる文字の頻度をヒストグラムにプリントするプログラムを書け。

って下記のような感じでよいのでしょうか? 問題の意味が少々解りづらかったものですから……。
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
  入力中の異なる文字の頻度をヒストグラムにプリントするプログラム;縦書き
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#include <stdio.h>

#define KINDS		26
#define DIF_S		97
#define DIF_L		65

main()
{
	int c, aChar[KINDS], i, j, eMax;

	/* 初期化 */
	for( i = 0; i < KINDS; ++i )
		aChar = 0;
	eMax = 0;

	/* 度数を記録 */
	while( (c = getchar()) != EOF )
	{
		if( c >= 'a' && c <= 'z' )
			++aChar[c - DIF_S];
		else if( c >= 'A' && c <= 'Z' )
			++aChar[c - DIF_[/url];
	}

	/* 最大度数を求める */
	for( i = 0; i < KINDS; ++i )
	{
		if( aChar > eMax )
			eMax = aChar;
	}

	/* ヒストグラムを出力 */
	for( i = 0; i < KINDS; ++i )
		printf( " %c", 'A' + i );
	printf( "\n" );
	for( i = 0; i < KINDS; ++i )
		printf( "--" );
	printf( "\n" );
	for( i = 0; i < eMax; ++i )
	{
		for( j = 0; j < KINDS; ++j )
		{
			if( aChar[j] >= eMax - i )
				printf( " *" );
			else
				printf( "  " );
		}
		printf( "\n" );
	}
	for( i = 0; i < KINDS; ++i )
		printf( "--" );
	printf( "\n" );
	for( i = 0; i < KINDS; ++i )
		printf( " %c", 'A' + i );
	printf( "\n" );

	getchar();	// 結果確認用
}



 あと,入力文字数が多いと上部が表示されないんですが,DOS窓って出力制限があるのですか?

Re:K&Rの演習問題

Posted: 2008年6月22日(日) 21:23
by box
サンプルの入力ファイルとして、お書きになったコードを
プログラムに食わせてみる、という方法があるでしょう。

>  あと,入力文字数が多いと上部が表示されないんですが,DOS窓って出力制限があるのですか?

コマンドプロンプトのプロパティで「高さ」を多く取ればよいと思います。

または、出力結果をテキストファイルにリダイレクトして、
後からエディタなどで確認する、という方法もあります。

hoge.exe < hoge.c > result.txt

のように。

Re:K&Rの演習問題

Posted: 2008年6月22日(日) 22:05
by Diaoler
 boxさん,回答ありがとうございますm(_ _)m

> コマンドプロンプトのプロパティで「高さ」を多く取ればよいと思います。

 おお,そうでした(^ ^;
 DOS窓にはプロパティがありましたね。


 ちなみに私は現在,ダンジョン自動生成アルゴリズムを用いたダンジョンRPGを制作中です。
 (C++とDXライブラリ使用)

 クラスやらポインタやらと奮闘中。。。