【问题标题】:How to call a function from a derived class in a base class?如何从基类中的派生类调用函数?
【发布时间】:2014-01-12 06:55:52
【问题描述】:

我花了好几个小时在网上寻找,但没有人和我一样的问题。基本上,我有一个名为 MainShop 的基类,它有 3 个派生类,分别是 SwordShop、SpellBookShop 和 BowShop。我希望基类能够从派生类之一调用函数,但无论我做什么,它似乎都不起作用! 这是我的代码:

#include "MainShop.h"
//BaseClass cpp

void MainShop::EnterShop(Hero& hero)
{
    //Display Choices
        switch (choice)
        {
            //Swords
            case 1: SwordShop::soldierShop(hero);//DOES NOT WORK!!
                        break;
            case 2: SpellBookShop::MageShop(hero);//Staffs
                        break;
            case 3: BowShop::ArcherShop(hero);//Bows
                        break;
            default: cout << "Error!, Please try again.";
                        MainShop::EnterShop(hero);


        }
}

我还有另外两个派生类,但它们的概念基本相同。我在其中一个派生类中有一个函数,我想从基类中调用它。这是我的派生类之一:

//SwordShop derived cpp
#include "SwordShop.h"
void SwordShop::soldierShop(Hero& hero)
{
  /* some code here*/
}

【问题讨论】:

  • 这就是虚函数的用途。
  • 是的,持有一个指向基类的指针,然后调用.shop()
  • 我认为您似乎采用结构化的 OO 方法进行构建非常棒,只要您觉得自己正在学习或朝着目标前进,您肯定应该继续沿着这条道路前进。然而,从长远来看,我的经验是继承和多态会导致程序不灵活和逻辑重复,尤其是当项目变得更大时。我有更紧凑、更敏捷的程序,具有类型特征和组合。诚然,进入的门槛更高,所以我说现在继续走这条路也不错。

标签: c++ class oop object inheritance


【解决方案1】:

我想你可以试试这样的:

Derived* derived = dynamic_cast<Derived*>(this);
if (derived) {
    // this is of Derived type
} else {
    // this is of base type but not Derived
}

虽然建议您最好使用虚函数,因为它是正确的用例:

class Base {
public:
    virtual void someMethod() = 0;

    void anotherMethods() {
        someMethod(); // determined by implementation in derived class
    }
};

class Derived1 : public Base {
    virtual void someMethod() override {
        // body
    }
};

class Derived2 : public Base {
    virtual void someMethod() override {
        // body
    }
};

更好的可读性,更不容易出错,更理智。

【讨论】:

    【解决方案2】:

    这样的事情怎么样? 显然你必须实现.shop()函数:

    MainShop *ms;
    switch(input){
        case 1:
        ms = new soldierShop(); break;
        case 2:
        ms = new MageShop(); break
        case 3:
        ms = new ArcherShop(); break;
    }
    ms.shop();
    

    【讨论】:

      【解决方案3】:

      通常有两个选项用于实现所需的功能:

      1. 使用 dynamic_cast 将 this 指针提升到所需的派生类型,并在转换成功时调用您想要的任何内容。
      2. 将模板化基类与奇怪重复的模板模式结合使用 - 基本上,将所需的派生类型作为模板参数传递给基类 (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)。

      【讨论】:

      • 还是虚函数?
      • 不。 :) 虚函数让您无法控制您调用的派生实例。在许多情况下,您需要调用特定的东西(这可能是 OP 一直在问的)。
      • 99% 确定 如果 OP 需要它,这是因为糟糕的设计。 OP 需要一个虚函数,如果没有,则需要重构。如果你不同意我的观点,我强烈建议你解释一下你的解决方案是好的做法,否则答案很可能会误导。
      • 什么是OP?如果我选择选项 2,我将不得不改变我的整个项目,而且我对 c++ 的经验并不丰富,所以我需要很长时间才能掌握它。然而,选项 1 看起来很有趣。你能给我一个如何使用选项1的例子吗?
      • 我同意这不是解决 OP 问题的方法,但我赞成摆脱 -1,因为从技术上讲,这些是“在基类”。
      【解决方案4】:

      在超类方法中选择特定的子类实例并不是一个好的设计,例如dynamic_cast,由于运行时开销和未来维护等。

      您可以将这种 switch-case 逻辑的负担卸载到由语言设计的虚拟函数,以通过基类指针/引用调用特定实例。

      例如:

      class MainShop
      {
      public:
         virtual void EnterShop(Hero &hero) = 0;
      };
      
      class SwordShop: public MainShop
      {
         void EnterShop(Hero &hero)
         {
            soldierShop(hero);
         }
      };
      
      class SpellBookShop: public MainShop
      {
         void EnterShop(Hero &hero)
         {
            MageShop(hero);
         }
      };
      
      int main()
      {
         ...
         MainShop *shop = new SwordShop;
         // calling soldierShop
         shop->EnterShop(hero);
         ..
         shop = new SpellBookShop;
         // calling MageShop
         shop->EnterShop(hero);
         ...
      }
      

      【讨论】:

      • 哦,好吧,我现在有点失望!非常清楚!但可以说我有一个英雄类,里面有一个叫做游戏的函数。主要我会明显地声明英雄实例并调用游戏玩法。在游戏内部,我希望能够调用这些商店,这就是我最初尝试使用 switch 语句的原因。如果不在 main 中声明每个派生类的每个实例,我怎么能做到这一点?
      • 这种情况下,您无论如何都需要将商店实例传递给游戏,因为您没有调用商店对象的静态方法,是吗?然后你可以将基类指针/引用作为它的参数传递给游戏。
      • 好的,那么在 main.cpp 中会是这样吗?英雄英雄;然后 hero.gameplay(MainShop& shop);在游戏内我会有 switch 语句?
      • 游戏内,调用虚函数:shop-&gt;..
      【解决方案5】:

      如果您需要从任何商店对象(SwordShop 等)调用 EnterShop,则可以在基类中覆盖虚函数。

      class MainShop
      {
           ...
           virtual void process_hero(Hero& hero)
           {
                // add default implementation or set as pure virtual
           }
           ...
      };
      
      void MainShop::EnterShop(Hero& hero)
      {
          process_hero(hero);
      }
      
      class SwordShop: public MainShop
      {
      public:
          void process_hero(hero)
          {
              soldierShop(hero);
          }
      };
      
      ...
      

      但是,在我看来,您希望管理器对象根据“选择”变量调用函数。如果是这种情况,请使用组合而不是继承。

      Prefer composition over inheritance?

      【讨论】:

      • 我看了作文,但我不知道发生了什么哈哈。我对 c++ 的经验还不是很丰富,你能给我一个在我的代码中使用组合的例子,以更好地理解这个概念吗?
      猜你喜欢
      • 1970-01-01
      • 2011-06-19
      • 1970-01-01
      • 1970-01-01
      • 2019-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-01
      相关资源
      最近更新 更多