【问题标题】:Reference to local variable returned对返回的局部变量的引用
【发布时间】:2020-07-26 13:10:01
【问题描述】:

我正在尝试为基于文本的游戏实现一个角色选择方法,我知道它不会像我那样工作,因为我返回一个对象的引用,该对象的生命周期仅限于方法调用。我还尝试在不引用 Fighter 父类并根据玩家角色选择返回子类(Samus 和 Ryu)的情况下实现该方法,但随后我会收到此错误:无效的抽象返回类型“Fighter”。

Fighter characterSelection(int player,bool checkForBot, Fighter &fig)
{
int input;
string newName;

if(checkForBot)
{
    input = (rand()%2)+1;
}
else{
    cout << "Please choose your Fighter player"<<player<<": \n1. Samus\n2. Ryu\n";
    input = readInput<int>();
}
if(input == 1)
{
    Samus sam;
    if(checkForBot)
    {
        cout << "Bot selected Samus!";
    }
    else{
        cout << "Player"<<player<<" selected Samus!\nDo you wish to change your fighters name?\n1.Yes\n2.No\n";
        input = readInput<int>();
        if(input == 1)
        {
            cout << "new Name: ";
            newName = readInput<string>();
            changeName(newName,sam);
        }
    }
    return sam;
}
else if(input == 2)
{
    Ryu ry;
    if(checkForBot)
    {
        cout << "Bot selected Ryu!";
    }
    else {
        cout << "Player"<<player<<" selected Ryu!\nDo you wish to change your fighters name?\n1.Yes\n2.No\n";
        input = readInput<int>();
        if(input == 1)
        {
            cout << "new Name: ";
            newName = readInput<string>();
            changeName(newName,ry);
        }
    }
    return ry;
 }
}

在选择了一个字符并结束函数调用后,将调用对象的析构函数,从而使引用链接到一个不存在的对象。

int main()
{
int input;
string newName;
bool checkForBot;
int player=1;
while(true)
{
    cout << "1. PVP \n2. PVE\n";
    input = readInput<int>();
    if(input == 1)
    {
       checkForBot = false;
        //Character Selection
        Fighter &fig1 = characterSelection(player,checkForBot,fig1);
        player++;
        Fighter &fig2 = characterSelection(player,checkForBot,fig2);
        //Battle
        fightPVP(fig1, fig2);

    }
    else if(input ==2)
    {
        //Character Selection
        Fighter &fig1 = characterSelection(player,checkForBot,fig1);
        checkForBot = true;
        Fighter &bot = characterSelection(player,checkForBot,bot);
        //Battle
        fightPVE(fig1, bot);
    }
}
return 0;
}

有没有其他方法可以解决这个问题,而不是引用父类,然后在函数调用中创建子类?

【问题讨论】:

  • 你从characterSelection函数返回什么?
  • 您正在(ab)使用允许引用右值的 MSVC 扩展。函数本身返回一个值,而不是引用。

标签: c++ polymorphism abstraction


【解决方案1】:

在这个:

Fighter &fig1 = characterSelection(player,checkForBot,fig1);

您正在引用函数返回的本地对象副本。

返回父对象也有对象slicing的问题;你应该考虑到这一点。

但是,如果这不是问题,您可以向类添加一个复制构造函数,并在声明接收变量时删除引用:

Fighter fig1 = characterSelection(player,checkForBot,fig1);

如果您的对象不适合复制,另一种方法是 new characterSelection 中的对象并返回基类的指针(或更好的共享指针)。

【讨论】:

    【解决方案2】:

    就您的代码而言,编译器是正确的。从函数“characterSelection”返回一个抽象类 Fighter 的实例,这是错误的,因为抽象类无法实例化。您只能将指针或引用(Fighter* 或 Figther&)返回到抽象类(指针通常是首选方式)。据我所知,使用从抽象类继承和在运行时选择一种 Fighter 来实现您想要的唯一方法是:

    #include <iostream>
    
    class Fighter
    {
        public:
            virtual void f() = 0;
            virtual ~Fighter() {};
    };
    
    class Samus : public Fighter
    {
        public:
            void f() { std:: cout << "Samus\n"; }
    };
    
    class Ryu : public Fighter
    {
        public:
            void f() { std:: cout << "Ryu\n"; }
    };
    
    Fighter* getFigther()
    {
        int fighterCode;
    
        std::cin >> fighterCode;
        switch (fighterCode)
        {
            case 0: return new Samus();
            case 1: return new Ryu();
            default: return nullptr;
        }
    }
    
    int main()
    {
        Fighter* myF = getFigther();
        
        // Do what you want with the fighter
        myF->f();
    
        // Release the resources
        delete myF;
    }
    

    如果你想避免任何原因的堆分配,你可以通过使用placement new语法来做到这一点:

    #include <iostream>
    
    class Fighter
    {
        public:
            virtual void f() = 0;
            virtual ~Fighter() {};
    };
    
    class Samus : public Fighter
    {
        public:
            void f() { std:: cout << "Samus\n"; }
    };
    
    class Ryu : public Fighter
    {
        public:
            void f() { std:: cout << "Ryu\n"; }
    };
    
    constexpr std::size_t size()
    {
        constexpr std::size_t samusS = sizeof(Samus);
        constexpr std::size_t ryuS = sizeof(Ryu);
    
        return samusS < ryuS ? ryuS : samusS;
    }
    
    void getFigther(Fighter*& f)
    {
        int fighterCode;
    
        std::cin >> fighterCode;
    
        // If figtherCode is invaild you can print a message or throw an exception
        switch (fighterCode)
        {
            case 0: new (f) Samus(); break;
            case 1: new (f) Ryu(); break;
        }
    }
    
    int main()
    {
        char space[size()];
        Fighter* myF = (Fighter*) (space);
        getFigther(myF);
        
        // Do what you want with the fighter
        // No delete requied
        myF->f();
    }
    

    但是,如果您有多个大小不同的战斗机类,则需要一个编译时函数来为您提供任何类(代表战斗机)的最大大小。在你的情况下,我认为这种方法不值得付出努力。

    【讨论】: