合計 昨日 今日

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

フォーラムルール
フォーラムルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Name: にほ
[URL]
かけだし(1,794 ポイント)
Date: 2018年2月08日(木) 20:11
No: 1
(OFFLINE)

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

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

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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;
};

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 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(); // ここでエラーが起きる
    }
}

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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();
};

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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 でした。

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

Name: かずま
[URL]
Date: 2018年2月09日(金) 18:10
No: 2
(OFFLINE)

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

にほ さんが書きました:このようなコードを実行すると、数回は成功するのですが、途中で以下のような例外が発生します。
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 でソートされます。
コード[C++]: 全て選択
1
2
3
    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>& にしましょう。

メンバ関数で実現しようとすれば、
コード[C++]: 全て選択
1
2
3
    bool operator() (const shared_ptr<Object>& right) const {
        return this->priority < right->priority;
    }

コード[C++]: 全て選択
1
2
3
    sort(obj.begin(), obj.end(),
        [](shared_ptr<Object>& a, shared_ptr<Object>& b) { return (*a)(b); }
    );

または、
コード[C++]: 全て選択
1
2
3
    bool operator<(const Object& right) const {
        return tpriority < right.priority;
    }

コード[C++]: 全て選択
1
2
3
    sort(obj.begin(), obj.end(),
        [](shared_ptr<Object>& a, shared_ptr<Object>& b) { return *a < *b; }
    );


外部関数にしてしまってもいいでしょう。
コード[C++]: 全て選択
1
2
3
bool lt(const shared_ptr<Object>& left, const shared_ptr<Object>& right) {
    return left->priority < right->priority;
}

コード[C++]: 全て選択
1
2
3
    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);
が必要です。

Name: かずま
[URL]
Date: 2018年2月09日(金) 18:18
No: 3
(OFFLINE)

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

かずま さんが書きました:外部関数にしてしまってもいいでしょう。
コード[C++]: 全て選択
1
2
3
bool lt(const shared_ptr<Object>& left, const shared_ptr<Object>& right) {
    return left->priority < right->priority;
}

コード[C++]: 全て選択
1
2
3
    sort(obj.begin(), obj.end(),
        [](shared_ptr<Object>& a, shared_ptr<Object>& b) { return lt(a, b); }
    );

外部関数を用意したら、ラムダ式は不要でした。
コード[C++]: 全て選択
1
    sort(obj.begin(), obj.end(), lt);

Name: にほ
[URL]
かけだし(1,794 ポイント)
Date: 2018年2月10日(土) 01:00
No: 4
(OFFLINE)

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

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

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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);
}

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 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();
    }
}

Name: にほ
[URL]
かけだし(1,794 ポイント)
Date: 2018年2月10日(土) 02:52
No: 5
(OFFLINE)

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

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

Name: にほ
[URL]
かけだし(1,794 ポイント)
Date: 2018年2月11日(日) 03:26
No: 6
(OFFLINE)

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

あれから何度か試行錯誤して分かったことがあります。決まって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の値が破壊されていることが分かりました。

Name: かずま
[URL]
Date: 2018年2月12日(月) 08:04
No: 7
(OFFLINE)

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

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

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

「論よりソース」です。


Return to C言語何でも質問掲示板

オンラインデータ

このフォーラムを閲覧中のユーザー: なし & ゲスト[19人]