模板类和嵌套类C ++

时间:2016-09-27 13:32:04

标签: c++ templates inner-classes

我对typename SnakeGame有疑问。我想知道如何在类SnakeGame中将KeyboardEvents设置为全局类型。现在,像DirectionKeyboard这样的嵌套类不知道类型SnakeGame是什么,因为它只看到KeyboardEvents<SnakeGame>类型。我不知道如何改变它:P

这是错误:

不知道来自'KeyboardEvents SnakeGame&gt;&amp;'的参数1的转换'SnakeGame&amp;'

我真的很感激帮助。

keyboardEvents.hpp

#include<SFML/Graphics.hpp>

template <typename SnakeGame>
class KeyboardEvents {
public:
    virtual ~KeyboardEvents() = default;

protected:
    class DirectionKeyboardEvent{
    public:
        virtual ~DirectionKeyboardEvent() = default;
        virtual void direction(SnakeGame&) = 0; // error no know conversion 
    };      

    class GoRight : public DirectionKeyboardEvent {
    public:
        void direction(SnakeGame& snakeObj) {
            snakeObj.snake[0].xCoor+=1;
        }
    };

    class GoRight : public DirectionKeyboardEvent {
    public:
        void direction(SnakeGame& snakeObj){
            snakeObj.snake[0].xCoor += 1;
        }
    };

    class GoLeft : public DirectionKeyboardEvent{
    public:
        void direction(SnakeGame& snakeObj){
            snakeObj.snake[0].xCoor-=1;
        }                     
    };                            

    class GoUp:public DirectionKeyboardEvent{
    public:
        void direction(SnakeGame& snakeObj){
            snakeObj.snake[0].yCoor-=1;
        }                   
    };

    class GoDown : public DirectionKeyboardEvent{
    public:
        void direction(SnakeGame& snakeObj){
            snakeObj.snake[0].yCoor+=1;
        }
    };

    std::map<sf::Keyboard::Key,  std::shared_ptr<DirectionKeyboardEvent>> mapOfDirects;

    void initializeDirectionMap() {
        mapOfDirects[sf::Keyboard::Right] = std::shared_ptr< DirectionKeyboardEvent >(new GoRight);
        mapOfDirects[sf::Keyboard::Left] = std::shared_ptr<DirectionKeyboardEvent>(new GoLeft);
        mapOfDirects[sf::Keyboard::Up] = std::shared_ptr<DirectionKeyboardEvent>(new GoUp);
        mapOfDirects[sf::Keyboard::Down] = std::shared_ptr<DirectionKeyboardEvent>(new GoDown);
    }

    void chooseMethodFromKeyboardArrows(sf::Keyboard::Key codeFromKeyboard) {
        auto iterator = mapOfDirects.find(codeFromKeyboard);

        if(iterator!=mapOfDirects.end()){
            iterator->second->direction(*this);//left , right,up , down, pause
            mainDirection=codeFromKeyboard;
        } else {
            mapOfDirects[mainDirection]->direction(*this);
        }
    }
};

这是我使用KeyboardEvents ~swockGame.hpp

的类
#include"keyboardEvents.hpp"

class SnakeGame:public Screen, public KeyboardEvents<SnakeGame> {
    public:
        SnakeGame(int size=16, int width=15, int height=15, int timeDelay=60000)
            : Screen(size, width, height), KeyboardEvents<SnakeGame>(), timeDelay(timeDelay) {}
};

2 个答案:

答案 0 :(得分:3)

您的设计似乎有点过于复杂。我认为这样的原因或许是你在进行时设计它。有时,如果你不得不坐下来思考这些事情,在白板上画出方框和线条会有所帮助。

在任何情况下,这都不是您问题的直接答案,而是基于我正在猜测您想要做的事情的替代方案的建议。

在我看来,你正试图实现一些通用键盘输入处理程序并将其绑定到你的游戏中。我可能完全错了,但如果没有,请考虑这样的事情。首先,接收键盘事件的通用接口。它不一定是模板,这对模板来说并不是一个很好的用例:

class KeyboardEventHandler {
public:
    enum Direction { Left, Right, Up, Down };
    virtual ~KeyboardEventHandler () { }
    virtual void onDirectionKey (Direction d) = 0;
};

现在处理键盘事件的SnakeGame可以继承它并实现自己的SnakeGame特定逻辑:

class SnakeGame : public KeyboardEventHandler {
public:
    void onDirectionKey (Direction d) {
        switch (d) {
        case Up: ...
        case Down: ...
        case Left: ...
        case Right: ...
        }
    }
};

然后你拥有的任何实际处理键盘事件和驱动所有这些的代码都可以使用KeyboardEventHandler *,这可能是SnakeGame,或者可能是你决定的任何其他内容将来使用它。

这只是组织的一种可能性。例如,你可以像这样构造它,打破KeyboardEvent,这可以简化未来的添加:

class KeyboardEvent {
public:
    enum Direction { Left, Right, Up, Down };
    Direction getDirection () { ... } // or whatever
};

class KeyboardEventHandler {
public:
    virtual ~KeyboardEventHandler () { }
    virtual void onEvent (KeyboardEvent &event) = 0;
};

SnakeGame为:

class SnakeGame : public KeyboardEventHandler {
public:
    void onEvent (KeyboardEvent &event) {
        ...
    }
};

如果你愿意的话,你可以在Direction / onDirectionKey之外命名那些东西,我从你的例子中选择了它,但只是在语义上做了一些也很方便的东西(例如,如果你打算扩展)它不仅仅包括箭头)。但无论如何,那就是重点。

还有10万种其他方法可以为这只猫提供皮肤,但重要的点是:如果你想为某些东西制作一些通用界面,你真的不能让它依赖于特定的什么继承它的细节,否则你的目的就是打破它的一般性。在这种情况下,要么它不是通用基础/继承的好例子,要么你只是拙劣的设计和需要坐下来重新思考。

请记住:您的目标不是为代码添加尽可能多的类和内容;你不是喜欢,继承高分。您的目标是保持代码清洁,可读,可维护,正确,可能可重用,并使您的工作更轻松。这些是工具,不要只使用它们,因为你拥有它们,而是在需要它们时使用它们来使你的生活更轻松。

然而,尽管如此,这对你的具体应用仍然有点过头了,虽然这是一个有趣的练习。说实话,在你的具体情况下,我只是把所有的继承和完全这样做并做了类似的事情:

class SnakeGame {
public:
    void handleKeyPress (char c) {
        // ... do the right thing here
    }
}

完成它。

答案 1 :(得分:3)

尝试在DirectionKeyboardEvent::direction课程中调用KeyboardEvents

即使您输入恰好是子类的模板参数,编译器也无法提前知道KeyboardEvents<SnakeGame>将完全由类SnakeGame扩展。

我的意思是,可以写下这段代码:

KeyboardEvents<SnakeGame> keyboardEvents;

keyboardEvents.chooseMethodFromKeyboardArrows(/* some key */);

在这种情况下,keyboardEventsSnakeGame的关系不大。实际上根本没有创建SnakeGame个实例!编译器是正确的,调用chooseMethodFromKeyboardArrows的函数direction错误地认为KeyboardEvents<SnakeGame>SnakeGame

继承工作相反:SnakeGame确实是KeyboardEvents<SnakeGame>。另一种方式是假的。

我可以告诉你&#34;如何让它工作&#34; ,但是这里需要一个警告:你过度使用继承,而你在错误的情况下使用了它KeyboardEvent。你真的应该尝试重新安排周围的事情,否则你将陷入一团糟。

解决方案&#34;使其工作&#34;

由于您正在使用CRTP,因此您可以告诉编译器KeyboardEvents<SnakeGame>确实在绝对所有情况下都被SnakeGame扩展。如果情况确实如此,那么你可以{@ 1}}你的基类到子类:

static_cast

稍好的解决方案

您也可以使用snake类的现有实例作为参数。

if(iterator!=mapOfDirects.end()){
    // Notice the presence of the cast here
    iterator->second->direction(static_cast<SnakeGame&>(*this));
    mainDirection=codeFromKeyboard;
}

但是,最好的办法是不要void chooseMethodFromKeyboardArrows(sf::Keyboard::Key codeFromKeyboard, SakeGame& game){ auto iterator = mapOfDirects.find(codeFromKeyboard); if(iterator!=mapOfDirects.end()){ iterator->second->direction(game); mainDirection=codeFromKeyboard; } else { mapOfDirects[mainDirection]->direction(game); } } 延长SnakeGame,而是将其包含在课程中:

KeyboardEvent

这是给你的功课: 尝试使类struct SnakeGame : Screen { KeyboardEvent<SnakeGame> event; void callEvents() { event.chooseMethodFromKeyboardArrows(/* some key */, *this); } }; 不是模板。我确定你可以找到一种方法来传递你的课而不使用themplates,同时仍然可以直接访问你的班级KeyboardEvent,而无需使用强制转换或接口。