【问题标题】:Returning a polymorphic class返回一个多态类
【发布时间】:2017-05-13 17:30:10
【问题描述】:

我正在创建一个小 Xbox 应用程序,一开始它应该检测当前正在运行的游戏,然后绕过和/或启动程序。由于我最近学习了多态性,我认为我可以在这里实现它以提高效率。下面是我的代码(没有我想象的那么高效):

DWORD(__cdecl *XamGetCurrentTitleID)() = (DWORD(__cdecl *)())ResolveFunction("xam.xex", 0x1CF); // Resolves current Game ID

typedef enum _XBOX_GAMES : DWORD // Enum that holds Game IDs
{
    BOOT_UP = 0x00000000,
    DASHBOARD = 0xFFFE07D1,
    FREESTYLEDASH = 0xF5D20000,
    COD_WORLD_AT_WAR = 0x4156081C,
    COD_MODERN_WARFARE = 0x415607E6,
    COD_MODERN_WARFARE_2 = 0x41560817,
    COD_BLACK_OPS_1 = 0x41560855,
    COD_MODERN_WARFARE_3 = 0x415608CB,
    COD_BLACK_OPS_2 = 0x415608C3,
    COD_GHOSTS = 0x415608fC,
    COD_ADVANCED_WARFARE = 0x41560914,
    COD_BLACK_OPS_3 = 0x4156091D,
    DESTINY = 0x415608F8,
    GTAV = 0x545408A7
} XBOX_GAMES;

DWORD GameChecker()
{
    DWORD lastID = NULL;

    Game *game;
    AW aw; BO1 bo1; BO2 bo2; BO3 bo3; Ghosts ghosts; MW2 mw2; MW3 mw3; Dashboard dashboard;

    for (;;)
    {
        if (XamGetCurrentTitleID() != lastID)
        {
            switch (XamGetCurrentTitleID())
            {
                case BOOT_UP:
                    // nothing
                    break;
                case DASHBOARD:
                    game = &dashboard;
                    game->Launch();
                    break;
                case COD_MODERN_WARFARE_2:
                    game = &mw2;
                    game->Launch();
                    break;
                case COD_MODERN_WARFARE_3:
                    game = &mw3;
                    game->Launch();
                    break;
                case COD_GHOSTS:
                    game = &ghosts;
                    game->Bypass();
                    game->Launch();
                    break;
                case COD_BLACK_OPS_1:
                    game = &bo1;
                    game->Launch();
                    break;
                case COD_BLACK_OPS_2:
                    game = &bo2;
                    game->Bypass();
                    break;
                case COD_BLACK_OPS_3:
                    game = &bo3;
                    game->Bypass();
                    break;
                case COD_ADVANCED_WARFARE:
                    game = &aw;
                    game->Bypass();
                    game->Launch();
                    break;
            }

            lastID = XamGetCurrentTitleID();
        }
    }

    return 0;
}

如您所见,我本可以将 game->Launch() 和 game->Bypass() 移到 switch 语句之外,以减少代码并且只调用一次,但我不知道我该怎么做确定游戏何时需要绕过和/或启动。这是我的 GameClass:

class Game
{
public:
    virtual void Bypass() { }

    virtual void Launch() { }
};

这是一个示例游戏的类:

// needs bypass and launch called
class AW : public Game
{
public:
    void Bypass()
    {
        // bypass
    }

    void Launch()
    {
        Sleep(500);
        XNotifyUI(L"AW - Loaded!");
    }
};

我想知道是否有办法做这样的事情:

DWORD GameChecker()
{
    DWORD lastID = NULL;

    Game *game;

    for (;;)
    {
        if (XamGetCurrentTitleID() != lastID)
        {
            switch (XamGetCurrentTitleID())
            {
                case BOOT_UP:
                    // nothing
                    break;
                default:
                    game = &functionThatReturnsClassBasedOnGame(XamGetCurrentTitleID()); // I don't know how to code such an function
                    break;
            }

            if (game->needsBypass)
                game->Bypass();

            if (game->needsLaunch)
                game->Launch();

            lastID = XamGetCurrentTitleID();
        }
    }

    return 0;
}

非常感谢任何帮助!感谢您的宝贵时间。

【问题讨论】:

  • typedefing 枚举是没有意义的。当您提供基础时,您失去了 C 兼容性。 C++ 已经将foo 识别为enum foo
  • _XBOX 这不是有效的标识符名称。在任何范围内,以下划线后跟大写字母开头的名称保留用于标准实现。

标签: c++ class polymorphism


【解决方案1】:

一个简单的方法来完成你的例子是这样的:

const int NEEDS_BYPASS = 1;
const int NEEDS_LAUNCH = 2;

class Game
{
public:
    virtual ~Game() { }
    virtual int getFeatures() const { return 0; }
    virtual void Bypass() { }
    virtual void Launch() { }
};

class Game_Dashboard : public Game
{
public:
    int getFeatures() const { return NEEDS_LAUNCH; }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"Dashboard - Loaded!");
    }
};

class Game_ModernWarfare2 : public Game
{
public:
    int getFeatures() const { return NEEDS_LAUNCH; }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"MW2 - Loaded!");
    }
};

class Game_ModernWarfare3 : public Game
{
public:
    int getFeatures() const { return NEEDS_LAUNCH; }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"MW3 - Loaded!");
    }
};

class Game_Ghosts : public Game
{
public:
    int getFeatures() const { return NEEDS_BYPASS | NEEDS_LAUNCH; }
    void Bypass() {
        // bypass...
    }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"Ghosts - Loaded!");
    }
};

class Game_BlackOpts1 : public Game
{
public:
    int getFeatures() const { return NEEDS_LAUNCH; }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"BO1 - Loaded!");
    }
};

class Game_BlackOpts2 : public Game
{
public:
    int getFeatures() const { return NEEDS_BYPASS; }
    void Bypass() {
        // bypass...
    }
};

class Game_BlackOpts3 : public Game
{
public:
    int getFeatures() const { return NEEDS_BYPASS; }
    void Bypass() {
        // bypass...
    }
};

class Game_AdvancedWarfare : public Game
{
public:
    int getFeatures() const { return NEEDS_BYPASS | NEEDS_LAUNCH; }
    void Bypass() {
        // bypass
    }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"AW - Loaded!");
    }
};

Game* getGame(DWORD ID)
{
    switch (ID)
    {
    case DASHBOARD:
        return new Game_Dashboard;
    case COD_MODERN_WARFARE_2:
        return new Game_ModernWarfare2;
    case COD_MODERN_WARFARE_3:
        return new Game_ModernWarfare3;
    case COD_GHOSTS:
        return new Game_Ghosts;
    case COD_BLACK_OPS_1:
        return new Game_BlackOpts1;
    case COD_BLACK_OPS_2:
        return new Game_BlackOpts2;
    case COD_BLACK_OPS_3:
        return new Game_BlackOpts3;
    case COD_ADVANCED_WARFARE:
        return new Game_AdvancedWarfare;
    }
    return NULL;
}

DWORD GameChecker()
{
    DWORD currentID, lastID = 0;
    Game *game;

    do
    {
        currentID = XamGetCurrentTitleID();
        if (currentID != lastID)
        {
            lastID = currentID;

            game = getGame(currentID);
            if  (game)
            {
                if (game->getFeatures() & NEEDS_BYPASS)
                    game->Bypass();

                if (game->getFeatures() & NEEDS_LAUNCH)
                    game->Launch();

                delete game;
            }
        }
    }
    while (true);

    return 0;
}

但是,更好地使用多态性应该更像这样:

class Game
{
public:
    virtual ~Game() { }
};

class CanBypass
{
public:
    virtual void Bypass() = 0;
};

class CanLaunch
{
public:
    virtual void Launch() = 0;
};

class Game_Dashboard : public Game, public CanLaunch
{
public:
    void Launch() {
        Sleep(500);
        XNotifyUI(L"Dashboard - Loaded!");
    }
};

class Game_ModernWarfare2 : public Game, public CanLaunch
{
public:
    void Launch() {
        Sleep(500);
        XNotifyUI(L"MW2 - Loaded!");
    }
};

class Game_ModernWarfare3 : public Game, public CanLaunch
{
public:
    void Launch() {
        Sleep(500);
        XNotifyUI(L"MW3 - Loaded!");
    }
};

class Game_Ghosts : public Game, public CanBypass, public CanLaunch
{
public:
    void Bypass() {
        // bypass...
    }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"Ghosts - Loaded!");
    }
};

class Game_BlackOpts1 : public Game, public CanLaunch
{
public:
    void Launch() {
        Sleep(500);
        XNotifyUI(L"BO1 - Loaded!");
    }
};

class Game_BlackOpts2 : public Game, public CanBypass
{
public:
    void Bypass() {
        // bypass...
    }
};

class Game_BlackOpts3 : public Game, public CanBypass
{
public:
    void Bypass() {
        // bypass...
    }
};

class Game_AdvancedWarfare : public Game, public CanBypass, public CanLaunch
{
public:
    void Bypass() {
        // bypass
    }
    void Launch() {
        Sleep(500);
        XNotifyUI(L"AW - Loaded!");
    }
};

Game* getGame(DWORD ID)
{
    switch (ID)
    {
    case DASHBOARD:
        return new Game_Dashboard;
    case COD_MODERN_WARFARE_2:
        return new Game_ModernWarfare2;
    case COD_MODERN_WARFARE_3:
        return new Game_ModernWarfare3;
    case COD_GHOSTS:
        return new Game_Ghosts;
    case COD_BLACK_OPS_1:
        return new Game_BlackOpts1;
    case COD_BLACK_OPS_2:
        return new Game_BlackOpts2;
    case COD_BLACK_OPS_3:
        return new Game_BlackOpts3;
    case COD_ADVANCED_WARFARE:
        return new Game_AdvancedWarfare;
    }
    return NULL;
}

DWORD GameChecker()
{
    DWORD currentID, lastID = 0;
    Game *game;
    CanBypass *bypasser; 
    CanLaunch *launcher;

    do
    {
        currentID = XamGetCurrentTitleID();
        if (currentID != lastID)
        {
            lastID = currentID;

            game = getGame(currentID);
            if  (game)
            {
                bypasser = dynamic_cast<CanBypass*>(game);
                if (bypasser)
                    bypasser->Bypass();

                launcher = dynamic_cast<CanLaunch*>(game);
                if (launcher)
                    launcher->Launch();

                delete game;
            }
        }
    }
    while (true);

    return 0;
}

【讨论】:

  • pastebin.com/ZgdDLHBW 在打印“Setting Bypasser / Launcher ...”后抛出一个致命异常(当我在仪表板时),所以我猜这里的演员或其他东西是错误的:bypasser = dynamic_cast(游戏);
  • 您能检查一下您提供的代码吗?它不工作:(
  • @ZZ_James:我给你的代码可以正常工作,我测试过。如果它为您崩溃,那么您没有正确地将它合并到您的项目中,但是您没有显示所有代码,只显示您的检查器循环。顺便说一句,由于您注释掉了 delete game; 语句,因此您的循环正在泄漏内存。为什么你使用currentID = XamGetCurrentTitleID() != 0 ? XamGetCurrentTitleID() : 0; 而不是currentID = XamGetCurrentTitleID();?您对?: 的使用是多余的。
猜你喜欢
  • 1970-01-01
  • 2012-07-24
  • 1970-01-01
  • 2011-09-09
  • 2022-11-15
  • 2018-04-25
  • 1970-01-01
  • 2013-08-30
  • 2013-04-30
相关资源
最近更新 更多