関数にアクセスできない例外

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

関数にアクセスできない例外

#1

投稿記事 by にほ » 2ヶ月前

現在、龍神録の館、龍神録2の館、新ゲームプログラミングの館を参考にシューティングゲームを作っています。そこで、ウィンドウ内にオブジェクトを表示させようと思ってコードをかいたのですが、関数にアクセスできず例外が発生してしまいます。

コード: 全て選択

// SceneGame.h

#pragma once

#include <vector>
#include <memory>

#include "Object.h"
#include "BaseScene.h"

class SceneGame : public BaseScene {
	vector<shared_ptr<Object>> obj;
	int stage;
public:
	SceneGame(ISceneChanger * ch, const Parameter& parameter);
	~SceneGame();
	void Update();
	void Draw() const;
};

コード: 全て選択

// SceneGame.cpp

#include <DxLib.h>

#include "Pad.h"
#include "Define.h"
#include "SceneGame.h"
#include "Frame.h"
#include "Field.h"

SceneGame::SceneGame(ISceneChanger *ch, const Parameter& parameter) : BaseScene(ch, parameter) {
	float arrx[4] = { 0, 0, 0, 416 };
	float arry[4] = { 0, 16, 464, 0 };

	for (int i = 0; i < 4; i++) {
		// 枠オブジェクトを生成
		obj.push_back(make_shared<Frame>(i, arrx[i], arry[i]));
	}
	// フィールドを生成
	// この一文を書き足すとエラーが出るようになる
	obj.push_back(make_shared<Field>((float)Define::FX, (float)Define::FY));
	// 描画優先度によってソート
	sort(obj.begin(), obj.end());
}

SceneGame::~SceneGame() {
	for (unsigned i = 0; i < obj.size(); i++) {
		obj[i].reset();
	}
	obj.clear();
}

void SceneGame::Update() {
	for (unsigned i = 0; i < obj.size(); i++) {
		obj[i]->Update();
	}
	if (Pad::get(PAD_START) == 1) {
		Parameter parameter;
		changer->ChangeScene(Scene_Title, parameter, TRUE);
	}
}

void SceneGame::Draw() const {
	for (unsigned i = 0; i < obj.size(); i++) {
		obj[i]->Draw(); // ここでエラーが起きる
	}
}

コード: 全て選択

// Object.h

#pragma once

#include <memory>

using namespace std;

class Object {
protected:
	float x, y;
	int priority = 0;
public:
	Object();
	Object(float _x, float _y);
	~Object() = default;
	virtual void Update() = 0;
	virtual void Draw() const = 0;
	bool operator() (const shared_ptr<Object> left, const shared_ptr<Object> right) const {
		return left->priority < right->priority;
	}
	void setPos(float _x, float _y);
	void addPos(float _x, float _y);
	float getX();
	float getY();
};

コード: 全て選択

// Field.h

#pragma once

#include <vector>
#include <memory>

#include "Object.h"

using namespace std;

class Field : public Object {
	vector<shared_ptr<Object>> obj;
	int i;
public:
	Field() = default;
	Field(float _x, float _y);
	~Field() = default;
	void Update();
	void Draw() const;
};
このようなコードを実行すると、数回は成功するのですが、途中で以下のような例外が発生します。
std::vector<std::shared_ptr<Object>,std::allocator<std::shared_ptr<Object> > >::operator[](...)._Ptr-> が 0xB5B8 でした。

最初の数回は期待通りの実行結果が得られるので、関数自体にアクセスすることは可能なのですが、その後アクセスできなくなっているようです。原因がわかる方、どなたかご教授いただければ幸いです。

かずま

Re: 関数にアクセスできない例外

#2

投稿記事 by かずま » 2ヶ月前

にほ さんが書きました: このようなコードを実行すると、数回は成功するのですが、途中で以下のような例外が発生します。
std::vector<std::shared_ptr<Object>,std::allocator<std::shared_ptr<Object> > >::operator[](...)._Ptr-> が 0xB5B8 でした。

最初の数回は期待通りの実行結果が得られるので、関数自体にアクセスすることは可能なのですが、その後アクセスできなくなっているようです。原因がわかる方、どなたかご教授いただければ幸いです。
Frame::Update や Field::Update がどうなっているのかの記述がないので、
SceneGame::obj がどんなふうに破壊されているのかが分かりません。

提示されたコードから分かることは、
SceneGame のコンストラクタの sort が間違っていることです。

sort(obj.begin(), obj.end()); で obj をソートしようとしていますが、
obj は shared_ptr の vector なので、ポインタの大小でソートされます。

ポインタが指す Object (Frame または Field) を priority でソートしたいらしくて、
Object の中で operator() を定義していますが、それは使われません。

次のように書けば、Object::operator() が呼び出されて、
priority でソートされます。

コード: 全て選択

    sort(obj.begin(), obj.end(),
        [](shared_ptr<Object>& a, shared_ptr<Object>& b) { return (*a)(a, b); }
    );
でも、operator() が Object のメンバ関数というのは変ですよね。
メンバ関数は、メンバ変数を参照したり変更するためにあります。
自身のメンバ変数は参照せず、引数で与えられた 2つのオブジェクトの比較を
だけを行うとはどういうことでしょうか?
どうしてもこのメンバ関数を使うのならということで、(*a)(a, b) のようにしました。

また Object::operator()関数の引数の型を const shared_ptr<Object> にして
いますね。これだと、呼び出し時に shared_ptr のコピーが起こり、
shared_ptr の持っている参照カウントが 1 増えます。
関数を出るときは、参照カウントが 1 減ります。これらの余計な処理を
しないで済むように、参照 const shared_ptr<Object>& にしましょう。

メンバ関数で実現しようとすれば、

コード: 全て選択

    bool operator() (const shared_ptr<Object>& right) const {
        return this->priority < right->priority;
    }

コード: 全て選択

    sort(obj.begin(), obj.end(),
        [](shared_ptr<Object>& a, shared_ptr<Object>& b) { return (*a)(b); }
    );
または、

コード: 全て選択

    bool operator<(const Object& right) const {
        return tpriority < right.priority;
    }

コード: 全て選択

    sort(obj.begin(), obj.end(),
        [](shared_ptr<Object>& a, shared_ptr<Object>& b) { return *a < *b; }
    );
外部関数にしてしまってもいいでしょう。

コード: 全て選択

bool lt(const shared_ptr<Object>& left, const shared_ptr<Object>& right) {
	return left->priority < right->priority;
}

コード: 全て選択

	sort(obj.begin(), obj.end(),
		[](shared_ptr<Object>& a, shared_ptr<Object>& b) { return lt(a, b); }
	);
ただし、protectd の priority にアクセスできないので、class Object の中に
friend bool lt(const shared_ptr<Object>& left, const shared_ptr<Object>& right);
が必要です。

かずま

Re: 関数にアクセスできない例外

#3

投稿記事 by かずま » 2ヶ月前

かずま さんが書きました: 外部関数にしてしまってもいいでしょう。

コード: 全て選択

bool lt(const shared_ptr<Object>& left, const shared_ptr<Object>& right) {
	return left->priority < right->priority;
}

コード: 全て選択

	sort(obj.begin(), obj.end(),
		[](shared_ptr<Object>& a, shared_ptr<Object>& b) { return lt(a, b); }
	);
外部関数を用意したら、ラムダ式は不要でした。

コード: 全て選択

	sort(obj.begin(), obj.end(), lt);

にほ
記事: 8
登録日時: 6ヶ月前

Re: 関数にアクセスできない例外

#4

投稿記事 by にほ » 2ヶ月前

返信ありがとうございます。sortの件、ありがとうございました。すべての文をコメントアウトした状態でも同様のエラーが発生したため、前の投稿には記述しておりませんでしたが、FieldとFrame内のコードということでしたので、以下に記します。

コード: 全て選択

// Frame.cpp

#include <DxLib.h>

#include "Frame.h"
#include "Resource.h"

Frame::Frame(int _id) : id((_id >= 0 && _id < 4) ? _id : 0) {
	priority = 0;
}

Frame::Frame(int _id, float _x, float _y) : Object(_x, _y) , id((_id >= 0 && _id < 4) ? _id : 0) {
	priority = 0;
}

void Frame::setId(int _id) {
	id = (_id >= 0 && _id < 4) ? _id : 0;
}

int Frame::getId() {
	return id;
}

void Frame::Update() {

}

void Frame::Draw() const {
	// 枠画像を表示
	DrawGraphF(x, y, Image::getBoard(id), TRUE);
}

コード: 全て選択

// Field.cpp

#include <DxLib.h>

#include "Field.h"
#include "Define.h"

Field::Field(float _x, float _y) : Object(_x, _y) {
	priority = 11;
}

void Field::Update() {
	for (unsigned i = 0; i < obj.size(); i++) {
		obj[i]->Update();
	}
}

void Field::Draw() const {
	// 仮の四角表示
	DrawBox(x, y, x + Define::FMX + 90, y + Define::FMY, 0x00ffff, TRUE);
	for (unsigned i = 0; i < obj.size(); i++) {
		// フィールド内のオブジェクト(プレイヤーや弾など)を描画
		obj[i]->Draw();
	}
}

にほ
記事: 8
登録日時: 6ヶ月前

Re: 関数にアクセスできない例外

#5

投稿記事 by にほ » 2ヶ月前

先ほど試したのですが、FieldクラスとGameSceneクラスのshared_ptrをunique_ptrに変更したところ、今のところエラーは置きなくなりました。どこかで予期せぬ参照が発生し、インスタンスが正しく破棄されていなかったのでしょうか。
一応このままunique_ptrを使ってコーディングしていきますが、エラーが完全になくなったわけではないのと納得のいく原因がわかっていないので解決済みにはせず、皆様のご意見をお伺いしたいと思います。

にほ
記事: 8
登録日時: 6ヶ月前

Re: 関数にアクセスできない例外

#6

投稿記事 by にほ » 2ヶ月前

あれから何度か試行錯誤して分かったことがあります。決まってobj[2]が破壊されるという事です(確定というわけではなく、他が確認できていない状態です)。priorityを変更してFieldクラスが先頭に並ぶようにしても同様の結果になりました。この時sortをしているかどうかにもかかわらず、破壊される要素は同じ場所です。
また、その時のデータが以下のようになっていました。

obj[2] shared_ptr {x=0.000000000 y=6.236e-39#DEN priority=33554432 } [1 strong ref] [make_shared]

xの値は正常ですが、yとpriorityの値が破壊されていることが分かりました。

かずま

Re: 関数にアクセスできない例外

#7

投稿記事 by かずま » 2ヶ月前

unique_ptr に変更すると、エラーが起きなくなるとか、
obj[2] の y と priority が破壊されるとか、
情報を小出しにされても、問題の解決には程遠いように思います。

すべての文をコメントアウトした状態でも同様のエラーが発生する
のなら、ソースは短くなるはずです。
それ全体を貼り付けることはできないでしょうか?

「論よりソース」です。

返信

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