2016-08-26 13 views
1

この質問はthis質問から来ます。 私はコンテナ(ゲーム)にshared_ptrを持つ状態パターンを実装しようとしています。C++状態パターンで宣言する場所を知る方法

しかし、私は円形の包含に問題があり、宣言を転送する必要があります。

マイコード:

Game.h

#pragma once 
#include <memory> 

#include "BaseState.h" 
class Game : public std::enable_shared_from_this<Game> 
{ 
private: 
    std::shared_ptr<BaseState> currentState; 
public: 
    Game(); 
    void switchState(std::shared_ptr<BaseState> nextState); 
    void doSomething(char); 
    void runState(); 
}; 

CPP

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

#include "Game.h" 
#include "SomeState.h" 

Game::Game() 
{ 
    currentState = std::make_shared<SomeState>(); 
} 

void Game::switchState(std::shared_ptr<BaseState> nextState) 
{ 
    currentState = nextState; 
} 

void Game::doSomething(char c) 
{ 
    std::cout << "Game : " << c; 
} 

void Game::runState() 
{ 
    currentState->handleCommand(shared_from_this()); 
} 

BaseState.h

#pragma once 
#include <memory> 

#include "Game.h" 

class BaseState 
{ 
public: 
    virtual void handleCommand(std::shared_ptr<Game>) = 0; 
}; 

SomeState.h

#pragma once 
#include "BaseState.h" 
class SomeState : 
    public BaseState 
{ 
public: 

    // Inherited via BaseState 
    virtual void handleCommand(std::shared_ptr<Game>) override; 
}; 

cppの前方宣言が、まだそれを得ることはありませんについて

#include "stdafx.h" 
#include "SomeState.h" 

void SomeState::handleCommand(std::shared_ptr<Game> game) 
{ 
    game->doSomething('S'); 
} 

I read他の質問。

私は何を試しましたか。

forward宣言BaseStateゲームでは、コードはコンパイルされますが、エラーはスローされます。 ConsoleApplication1.exeで0x73E9DAE8で

未処理の例外: のMicrosoft C++の例外:メモリ位置 0x00BBF5D4でのstd :: bad_weak_ptr。

はフォワードBaseStateゲームを宣言します。 Dosntコンパイルも、未定義の型エラーの使用を与える

「doSomethingのは、」:doSomethingのためにコンパイル時の試合でロジックです 「のstd :: shared_ptrの」

のメンバーていないではありません関数のように宣言されているため;私はどこ別のクラスを宣言転送先を決めるん

class Game; 

はどのように、どの論理的なステップがありますか、私は1つだけ選んで作成チョイスの問題を修正する必要がありますか?あなたはBaseState.h#include <Game.h>する必要はありません

+2

必要なクラスをヘッダファイルに宣言します。次に、cppファイルには実際に必要な宣言があるヘッダーファイルが含まれています。これは、デュープターゲットのジストです。 – NathanOliver

+0

'BaseState.h'ファイルは本当に完全な' Game'クラスを必要とせず、前方宣言だけです。 'Game.h'と同じですが、完全な' BaseState'定義は必要なく、前方宣言だけです。 –

+1

クラッシュについては、あなたが持っている前方宣言の問題とは無関係です。その代わりに、デバッガを使用してコード内のクラッシュを特定し、その原因を突き止めてください。 –

答えて

3

、あなたは単にBaseState宣言はGameの内容を知っている必要はありませんので、これが機能すること

class Game; 

、前方宣言することができます。最初に試したことはOKです。同じことがGame.h#include <BaseState.h>にも当てはまります。それを前方宣言BaseStateに置き換えてください。

std::bad_weak_ptr例外は何か他のものが原因でした。具体的には、おそらく

それだけのstd :: shared_ptrの管理するオブジェクトの以前の共有 オブジェクト、つまり上shared_from_thisを呼び出すことが許可されていると言うshared_from_thisについて少し詳細を逃しています。そうでなければ 挙動は

のstd :: bad_weak_ptr(デフォルト-構築weak_thisから shared_ptrのコンストラクタで)スローされます(C++ 17から)

を定義されていません

通常、オブジェクトをshared_ptrにインスタンス化することでこれを解決できます。

int main() { 
    auto myGame = std::make_shared<Game>(); 
    . . . 
    myGame->runState(); 
    . . . 
} 

EDIT

shared_ptrがそれを使用して関連付けられた特定のコストを持っていること、しかし心に留めておいてください。一般的には、指さしオブジェクトが使用されている関数呼び出しよりも先に指定されていることがわかっている場合は、BaseState::handleCommandの場合のように、参照で渡すほうが高速(かつ安全です)かもしれません。

+0

不要な円形のインクルードがまだあります。 forward-declareは 'Base.hate.h'を' Game.h'ヘッダの中に含める必要性を避けます。代わりに、前方宣言は、 'Game.h'インクルードが' BaseState.h'の内部にあるのを避けるために書かれています。もちろん、ソースファイルは、移動したインクルードに適切に更新する必要があります。 – aruisdante

+0

一般に、 'BaseState'の設計は間違っています。 'BaseState'は' doingSomething'のときに 'Game'の所有権を取得しません。これは、もしそれがmutationを必要とするならば標準の 'Game&'、あるいはそうでなければ 'const Game&'を取るべきです。これにより、最初に 'shared_from_this()'の使用が不要になり、単に 'currentState-> doSomething(* this)'を呼び出すことができます。 – aruisdante

+0

@RustyX答えをくれてありがとう、どのようにBaseStateのゲームやゲームでBaseStateを選ぶか知っていますか?私はどのように選ぶかを決めるのが好きです。 –