クラスの配列の一部のみを消去したいのですが・・

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Makin
記事: 12
登録日時: 8年前

クラスの配列の一部のみを消去したいのですが・・

#1

投稿記事 by Makin » 8年前

今、学生のIDと名前を、登録または消去するプログラムを作っているのですが、学生の情報を消去をするときに、上手くクラスのコピーが行われずに、プログラムが途中で強制終了してしまいます。コードは、抽象クラスSchoolを定義して、それを継承したStudentを作りました。抽象クラスSchoolの配列を作り、その配列に継承クラスのStudentを指すポインタを入れています。コピーコンストラクタが問題なのかなと思い、コピーコンストラクタも作りましたが、以前と変わらずバグが生じてしまいます。
消去のコードのところで、一度schoolの配列の一つを消去して、また新しく同じクラスを作っていますが、抽象クラスを継承したstudent以外のクラス(student2など)も作りたいので、このように書いています。バグの原因を色々と調べましたが、自分では何が間違っているのか見当がつかないので質問させていただきました。よろしくお願いします。

コード:

#include "stdafx.h"
#include <iostream>
#include <string.h>

using namespace std;

//抽象クラス
class School {
protected:
	char ID[10];
public:
	char* getID() {
		return ID;
	}
	virtual void addData() {
		cin.getline(ID, 10);
	}
	virtual void showData() {
		cout << ID << endl;
	}
};

//継承クラス
class Student : public School {
	char name[30];
public:
	Student(){}
	Student(const Student &s) {
		strcpy_s(name, s.name);
		strcpy_s(ID, s.ID);
	}
	void addData() {
		cout << "IDを入力してください: ";
		cin.getline(ID, 10);
		cout << "名前を入力してください: ";
		cin.getline(name, 30);
	}
	void showData() {
		cout << ID << ' ' << name << endl;
	}
};

int main() {
	const unsigned maxStudent = 100;
	School *school[maxStudent];
	int numStudent = 0;
	int choice = 0;
	while (choice != 4) {
		cout << "1) 学生を登録する" << endl << "2) 学生のデータを消去する" << endl << "3)学生のデータを見る" << endl << "4) 終了" << endl;
		cin >> choice;
		while (getchar() != '\n');
     
     //学生を登録する
		if (choice == 1) {
			school[numStudent] = new Student;
			school[numStudent]->addData();
			numStudent++;
		}
		
		//学生のデータを消去する
		if(choice == 2) {
			char id[30];
			int flag = 0;
			cout << "IDを入力してください" << endl;
			cin >> id;
			while (getchar() != '\n');

			//消去したい生徒のIDが存在するか調べる
			int i;
			for (i = 0; i < numStudent; i++) {
				if (!strcmp(school[i]->getID(), id)) {
					flag = 1;
					break;
				}
			}

			//消去したい生徒のIDが見つかった場合
			if (flag == 1) {
				for (i; i < numStudent - 1; i++) {
					delete school[i];
					school[i] = new Student;
					school[i] = school[i + 1];
				}
				numStudent--;
				delete school[i];
			}

			//消去したい生徒のIDが見つからなかった場合
			if (flag == 0) {
				cout << "そのIDは存在しません" << endl;
			}
		}

     //登録した学生のデータを見る
		if (choice == 3) {
			for (int i = 0; i < numStudent; i++) {
				school[i]->showData();
			}
		}
	}
	return 0;
}

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: クラスの配列の一部のみを消去したいのですが・・

#2

投稿記事 by みけCAT » 8年前

Makin さんが書きました:消去のコードのところで、一度schoolの配列の一つを消去して、また新しく同じクラスを作っていますが、
一つではなく、見つかった要素以降全てを消去していますね。
Makin さんが書きました:

コード:

			//消去したい生徒のIDが見つかった場合
			if (flag == 1) {
				for (i; i < numStudent - 1; i++) {
					delete school[i];
					school[i] = new Student;
					school[i] = school[i + 1];
				}
				numStudent--;
				delete school[i];
			}
このコードが何をしているかを、i = 2, numStudent = 5の場合の例で図示します。

1. 初期状態
(ループ1周目:i = 2)
2. delete school;
3. school = new Student;
4. school = school[i + 1];
(ループ2周目:i = 3)
5. delete school;
6. school = new Student;
7. school = school[i + 1];
(ループの後)
8. delete school;

kurasunohairetunoitibunomiwosyoukyositainodesuga-20170423.png
提示されたコードの動作
kurasunohairetunoitibunomiwosyoukyositainodesuga-20170423.png (36.37 KiB) 閲覧数: 2321 回
このように、この消去操作を行うと、deleteで削除されたオブジェクトへのポインタが配列の有効とみなされる部分に格納されることになります。
deleteで削除されたオブジェクトにアクセス(ポインタをデリファレンスして読みor書き)してしまうと、そこに確保された他のオブジェクトのデータの破壊などが発生することがあり、危険です。
これをdangling pointerといいます。
また、新たに確保されたオブジェクトのポインタが格納された変数をすぐに他のポインタで上書きしてしまい、メモリリークも発生しています。

例えば、こうするといいでしょう。

コード:

			//消去したい生徒のIDが見つかった場合
			if (flag == 1) {
				delete school[i]; // その生徒を消去する
				for (i; i < numStudent - 1; i++) { // 残りの生徒のデータを前にずらす
					school[i] = school[i + 1];
				}
				numStudent--;
			}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Makin
記事: 12
登録日時: 8年前

Re: クラスの配列の一部のみを消去したいのですが・・

#3

投稿記事 by Makin » 8年前

みけCATさん、わざわざ図まで用意していただいて、とても分かりやすい説明をありがとうございます。頭の中でポインタの動きを全く把握できていませんでした。消去したい生徒以降のオブジェクトを毎回わざわざdeleteして、全く同じコピーを作ろうとしなくても、もともとschoolはポインタの配列なのだから、単に指すアドレスを変えれば済む話だったんですね。とても丁寧な説明で助かりました。ありがとうございました。

Makin
記事: 12
登録日時: 8年前

Re: クラスの配列の一部のみを消去したいのですが・・

#4

投稿記事 by Makin » 8年前

解決!のチェックを入れるのを忘れていました。

返信

“C言語何でも質問掲示板” へ戻る