C++で迷路を作成し、ファイル出力をするプログラムを作成しています。
このプログラムを実行中に例外がスローされ、正しく実行が行われません。
### 発生している問題・エラーメッセージ
main.cpp28行目
により ハンドルされない例外が 0x003AD509 (p3k4_1.exe) で発生しました: 0xC00000FD: Stack overflow (パラメーター: 0x00000001, 0x00C72FFC)。 が発生しました
### 該当のソースコード
// main.cpp
#include "MazeMap.h"
#include <iostream>
MazeMap::MazeMap(int w, int h) {
width = 2 * w + 1;
height = 2 * h + 1;
data = new char* [width];
for (int i = 0; i < width; ++i) {
data[i] = new char[height];
}
}
MazeMap::~MazeMap() {
for (int i = 0; i < width; ++i) {
delete[] data[i];
}
delete[] data;
}
int MazeMap::getWidth() {
return width;
}
int MazeMap::getHeight() {
return height;
}
bool MazeMap::isInside(int x, int y) {
if ((0 <= x) && (x < width) && (0 <= y) && (y < height)) {
//std::cout << "Inside true" << std::endl;
return true;
} else {
//std::cout << "Inside false" << std::endl;
return false;
}
}
bool MazeMap::isWall(int x, int y) {
if (isInside(x, y) == false) {
return false;
}
if (**data == wall) {
return true;
} else {
return false;
}
}
bool MazeMap::set(int x, int y, char c) {
if (isInside(x, y) == true) {
//std::cout << "Set true" << std::endl;
data[x][y] = c;
return true;
} else {
//std::cout << "Set false" << std::endl;
return false;
}
}
char MazeMap::get(int x, int y) {
return data[x][y];
}
void MazeMap::clear() {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++)
data[i][j] = wall;
}
}
//MaseMakerSimple.cpp
#include "MazeMakerSimple.h"
MazeMakerSimple::MazeMakerSimple(MazeMap* mazeMap) {
this->mazeMap = mazeMap;
}
void MazeMakerSimple::generate() {
mazeMap->clear();
// ランダムに迷路作成開始地点を選択(ただし,奇数座標)
int x = rand() % (mazeMap->getWidth() / 2) * 2 + 1;
int y = rand() % (mazeMap->getHeight() / 2) * 2 + 1;
// (x,y)の座標から迷路の作成開始
checkPoint(x, y);
// 左上にスタート地点、右下にゴール地点を置いて終了
mazeMap->set(0, 1, mazeMap->start);
mazeMap->set(mazeMap->getWidth() - 1, mazeMap->getHeight() - 2, mazeMap->goal);
}
void MazeMakerSimple::checkDirection(int x, int y, int d) {
// 4つの方向を示す構造体
static const struct pos {
int x, y;
} steps[] = {
{0, +1}, // 下
{+1, 0}, // 右
{0, -1}, // 上
{-1, 0} // 左
};
// 引数dの値により,steps[]で示される方向の1つを決定
int dx = steps[d & 3].x;
int dy = steps[d & 3].y;
if (mazeMap->isWall(x + 2 * dx, y + 2 * dy)) { // 指定された方向の2マス先が壁であれば
mazeMap->set(x + dx, y + dy); // その方向の1マス先を通路にセット
mazeMap->set(x + 2 * dx, y + 2 * dy); // その方向の2マス先を通路にセット
// 注目点を2マス先に移して,チェックを継続
checkPoint(x + 2 * dx, y + 2 * dy);
}
}
void MazeMakerSimple::checkPoint(int x, int y) {
if (!mazeMap->isInside(x, y)) { // 迷路の範囲外なら
return;
}
// 指定座標を通路にセット
mazeMap->set(x, y);
// 探索する方向を乱数で設定
int d = rand();
// 再探索の順番を設定(時計回り or 反時計回り)
int dd = (rand() & 1) * 2 - 1; // -1 or +1
// 4方向に通路が延ばせるか順番にチェック
for (int i = 0; i < 4; ++i) {
checkDirection(x, y, d);
d += dd;
}
}
//MazeMap.cpp
#include "MazeMap.h"
#include <iostream>
MazeMap::MazeMap(int w, int h) {
width = 2 * w + 1;
height = 2 * h + 1;
data = new char* [width];
for (int i = 0; i < width; ++i) {
data[i] = new char[height];
}
}
MazeMap::~MazeMap() {
for (int i = 0; i < width; ++i) {
delete[] data[i];
}
delete[] data;
}
int MazeMap::getWidth() {
return width;
}
int MazeMap::getHeight() {
return height;
}
bool MazeMap::isInside(int x, int y) {
if ((0 <= x) && (x < width) && (0 <= y) && (y < height)) {
//std::cout << "Inside true" << std::endl;
return true;
} else {
//std::cout << "Inside false" << std::endl;
return false;
}
}
bool MazeMap::isWall(int x, int y) {
if (isInside(x, y) == false) {
return false;
}
if (**data == wall) {
return true;
} else {
return false;
}
}
bool MazeMap::set(int x, int y, char c) {
if (isInside(x, y) == true) {
//std::cout << "Set true" << std::endl;
data[x][y] = c;
return true;
} else {
//std::cout << "Set false" << std::endl;
return false;
}
}
char MazeMap::get(int x, int y) {
return data[x][y];
}
void MazeMap::clear() {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++)
data[i][j] = wall;
}
}
//MazePrinter.cpp
#include "MazePrinter.h"
MazePrinter::MazePrinter(Display* display) {
this->display = display;
}
void MazePrinter::output(MazeMap* mazeMap) {
display->start(); // 表示前処理
for (int y = 0; y < mazeMap->getHeight(); ++y) {
for (int x = 0; x < mazeMap->getWidth(); ++x) {
display->put(mazeMap->get(x, y)); // 1文字表示処理
}
display->newLine(); // 改行処理
}
display->end(); // 表示後処理
}
//Display.cpp
#include "Display.h"
Display::~Display() {
}
//DisplayScreen.cpp
#include "DisplayScreen.h"
void DisplayScreen::start() {
}
void DisplayScreen::end() {
}
void DisplayScreen::put(char c) {
stream() << c;
}
void DisplayScreen::newLine() {
stream() << std::endl;
}
std::ostream& DisplayScreen::stream() {
return std::cout;
}
//DisplayFile.cpp
#include "DisplayFile.h"
DisplayFile::DisplayFile(const char* fileName) {
/* ここを実装してください */
fp = fopen(fileName, "w");
}
DisplayFile::~DisplayFile() {
/* ここを実装してください */
fclose(fp);
}
std::ostream& DisplayFile::stream() {
return ofs;
}
```
####DisplayFileWide.cpp
```c++
#include "DisplayFileWide.h"
void DisplayFileWide::put(char c) {
/* ここを実装してください */
stream() << c;
}
//MaseMakerSimple.h
#ifndef MAZEMAKERSIMPLE_H
#define MAZEMAKERSIMPLE_H
#include <cstdlib>
#include "MazeMap.h"
class MazeMakerSimple {
public:
/**
* コンストラクタ
* @param mazeMap MazeMapインスタンス
*/
MazeMakerSimple(MazeMap* mazeMap);
/**
* 迷路生成
*/
void generate();
private:
/** MazeMapインスタンス */
MazeMap* mazeMap;
/**
* 座標(x,y)において dで指定された方向をチェック
* @param x x座標
* @param y y座標
* @param d 方向を示すインデックス
*/
void checkDirection(int x, int y, int d);
/**
* 座標(x,y)において全方向をチェック
* @param x x座標
* @param y y座標
*/
void checkPoint(int x, int y);
};
#endif /* MAZEMAKERSIMPLE_H */
//MazeMap.h
#ifndef MAZEMAP_H
#define MAZEMAP_H
/**
* @class MazeMap
* @breif 迷路情報保持用クラス
*/
class MazeMap {
public:
/** 「壁」の表示 */
static const char wall = 'O';
/** 「通路」の表示 */
static const char passage = ' ';
/** 「スタート地点」の表示 */
static const char start = 'S';
/** 「ゴール地点」の表示 */
static const char goal = 'G';
/**
* コンストラクタ
* @param w 迷路幅の基底
* @param h 迷路高の基底
* @attention 実際の迷路幅は2w+1,迷路高は2h+1
*/
MazeMap(int w, int h);
/**
* デストラクタ
*/
virtual ~MazeMap();
/**
* 迷路幅用ゲッタ
* @return 迷路幅
*/
int getWidth();
/**
* 迷路高用ゲッタ
* @return 迷路高
*/
int getHeight();
/**
* @retval true 迷路の範囲内
* @retval false 迷路の範囲外
*/
bool isInside(int x, int y);
/**
* @retval true 壁
* @retval false 壁でない
* @attention 指定座標が迷路範囲外であればfalseを返す
*/
bool isWall(int x, int y);
/**
* 指定座標のマップ情報のセッタ
* @param c 要素
* @retval true 迷路の範囲内
* @retval false 迷路の範囲外
* @attention 要素を指定しない場合,通路とする
*/
bool set(int x, int y, char c = passage);
/**
* 指定座標のマップ情報のゲッタ
* @return マップ情報
* @attention 迷路範囲外の座標を指定した場合,'\0'を返す
*/
char get(int x, int y);
/**
* マップ情報の初期化
* @attention 全ての要素を「壁」にする
*/
void clear();
private:
/** 迷路幅 */
int width;
/** 迷路高 */
int height;
/** 迷路情報 */
char** data;
};
#endif /* MAZEMAP_H */
//MazePrinter.h
#ifndef MAZEPRINTER_H
#define MAZEPRINTER_H
#include "Display.h"
#include "MazeMap.h"
class MazePrinter {
public:
/**
* コンストラクタ
* @param display Displayインスタンスのポインタ
*/
MazePrinter(Display* display);
/**
* 出力
* @param mazeMap MazeMapインスタンスのポインタ
*/
virtual void output(MazeMap* mazeMap);
private:
/** Displayインスタンスのポインタ*/
Display* display;
};
#endif /* MAZEPRINTER_H */
//Display.h
#ifndef DISPLAY_H
#define DISPLAY_H
#include <ostream>
class Display {
public:
/**
* デストラクタ
*/
virtual ~Display();
/**
* 表示前処理(純粋仮想関数)
*/
virtual void start() = 0;
/**
* 表示後処理(純粋仮想関数)
*/
virtual void end() = 0;
/**
* 1文字出力処理 (純粋仮想関数)
* @param c 出力文字
*/
virtual void put(char c) = 0;
/**
* 改行処理(純粋仮想関数)
*/
virtual void newLine() = 0;
protected:
/**
* 出力先の参照(純粋仮想関数)
* @return 出力先の参照
*/
virtual std::ostream& stream() = 0;
};
#endif /* DISPLAY_H*/
//DisplayScreen.h
#ifndef DISPLAYSCREEN_H
#define DISPLAYSCREEN_H
#include <iostream>
#include "Display.h"
class DisplayScreen : public Display {
public:
virtual void start();
virtual void end();
virtual void put(char c);
virtual void newLine();
protected:
virtual std::ostream& stream();
};
#endif /* DISPLAYSCREEN_H */
//DisplayFile.h
#ifndef DISPLAYFILE_H
#define DISPLAYFILE_H
#include <fstream>
#include "DisplayScreen.h"
class DisplayFile : public DisplayScreen {
public:
DisplayFile(const char* fileName = "output.txt");
virtual ~DisplayFile();
protected:
virtual std::ostream& stream();
private:
std::ofstream ofs;
FILE* fp;
};
#endif /* DISPLAYFILE_H */
//DisplayFileWide.h
#ifndef DISPLAYFILEWIDE_H
#define DISPLAYFILEWIDE_H
#include "DisplayFile.h"
class DisplayFileWide : public DisplayFile {
public:
virtual void put(char c);
};
#endif /* DISPLAYFILEWIDE_H */
VisualStudio2019
ソースファイルは//でコメントアウトして分けてあります。
DisplayFileWideは一つの壁を"00"と、通路を" "(スペース二つ)で表現したものです。
DisplayFileとDisplayFileWideの"ここを実装してください"とある部分になにかしらの問題があるのだと踏んでいます。また、DisplayFileクラスでfpを宣言していますが、それを使わなく実装できるのであればそうしたいです。