【问题标题】:Can I call a base class's virtual function if I'm overriding it?如果我要覆盖它,我可以调用基类的虚函数吗?
【发布时间】:2010-10-14 21:58:30
【问题描述】:

假设我的课程 FooBar 设置如下:

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
        std::cout << x << std::endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        // I would like to call Foo.printStuff() here...
        std::cout << y << std::endl;
    }
};

正如代码中所注释的那样,我希望能够调用我正在覆盖的基类函数。在 Java 中有 super.funcname() 语法。这在 C++ 中可行吗?

【问题讨论】:

标签: c++ virtual-functions overriding


【解决方案1】:

如果有多个继承级别,您可以指定直接基类,即使实际实现处于较低级别。

class Foo
{
public:
  virtual void DoStuff ()
  {
  }
};

class Bar : public Foo
{
};

class Baz : public Bar
{
public:
  void DoStuff ()
  {
    Bar::DoStuff() ;
  }
};

在这个例子中,类 Baz 指定 Bar::DoStuff() 虽然类 Bar 不包含 的实现>DoStuff。这是一个细节,Baz不需要知道。

显然,调用 Bar::DoStuff 比调用 Foo::DoStuff 更好,以防以后版本的 Bar 也覆盖此方法。

【讨论】:

    【解决方案2】:

    是的,您可以调用它。在子类中调用父类函数的C++语法是

    class child: public parent {
      // ...
    
      void methodName() {
        parent::methodName(); // calls Parent class' function
      }
    };
    

    阅读更多关于函数overriding的信息。

    【讨论】:

      【解决方案3】:

      检查一下...

      #include <stdio.h>
      
      class Base {
      public:
         virtual void gogo(int a) { printf(" Base :: gogo (int) \n"); };    
         virtual void gogo1(int a) { printf(" Base :: gogo1 (int) \n"); };
         void gogo2(int a) { printf(" Base :: gogo2 (int) \n"); };    
         void gogo3(int a) { printf(" Base :: gogo3 (int) \n"); };
      };
      
      class Derived : protected Base {
      public:
         virtual void gogo(int a) { printf(" Derived :: gogo (int) \n"); };
         void gogo1(int a) { printf(" Derived :: gogo1 (int) \n"); };
         virtual void gogo2(int a) { printf(" Derived :: gogo2 (int) \n"); };
         void gogo3(int a) { printf(" Derived :: gogo3 (int) \n"); };       
      };
      
      int main() {
         std::cout << "Derived" << std::endl;
         auto obj = new Derived ;
         obj->gogo(7);
         obj->gogo1(7);
         obj->gogo2(7);
         obj->gogo3(7);
         std::cout << "Base" << std::endl;
         auto base = (Base*)obj;
         base->gogo(7);
         base->gogo1(7);
         base->gogo2(7);
         base->gogo3(7);
      
         std::string s;
         std::cout << "press any key to exit" << std::endl;
         std::cin >> s;
         return 0;
      }
      

      输出

      Derived
       Derived :: gogo (int)
       Derived :: gogo1 (int)
       Derived :: gogo2 (int)
       Derived :: gogo3 (int)
      Base
       Derived :: gogo (int)
       Derived :: gogo1 (int)
       Base :: gogo2 (int)
       Base :: gogo3 (int)
      press any key to exit
      

      最好的方法是使用 base::function 如@sth

      【讨论】:

      • 正如this Question 中所述,由于protected 继承,这不应该起作用。要强制转换基类指针,您必须使用公共继承。
      • 有趣。在阅读this answer 之后,我认为使用受保护的继承,Derived 派生自 Base 的事实仅对类本身可见,对外部也不可见。
      【解决方案4】:

      有时你需要调用基类的实现,当你不在派生函数中时......它仍然有效:

      struct Base
      {
          virtual int Foo()
          {
              return -1;
          }
      };
      
      struct Derived : public Base
      {
          virtual int Foo()
          {
              return -2;
          }
      };
      
      int main(int argc, char* argv[])
      {
          Base *x = new Derived;
      
          ASSERT(-2 == x->Foo());
      
          //syntax is trippy but it works
          ASSERT(-1 == x->Base::Foo());
      
          return 0;
      }
      

      【讨论】:

      • 请注意,您需要 x 的类型为 Base* 并使用 Base::Foo 语法才能工作
      【解决方案5】:

      如果你想从它的派生类调用基类的函数,你可以简单地在被覆盖的函数内部调用并提到基类名称(如 Foo::printStuff())。

      代码放在这里

      #include <iostream>
      using namespace std;
      
      class Foo
      {
      public:
          int x;
      
          virtual void printStuff()
          {
               cout<<"Base Foo printStuff called"<<endl;
          }
      };
      
      class Bar : public Foo
      {
      public:
          int y;
      
          void printStuff()
          {
              cout<<"derived Bar printStuff called"<<endl;
              Foo::printStuff();/////also called the base class method
          }
      };
      
      int main()
      {
          Bar *b=new Bar;
          b->printStuff();
      }
      

      同样,您可以在运行时使用该类的对象(派生或基类)确定要调用哪个函数。但这要求您在基类中的函数必须标记为虚拟。

      代码如下

      #include <iostream>
      using namespace std;
      
      class Foo
      {
      public:
          int x;
      
          virtual void printStuff()
          {
               cout<<"Base Foo printStuff called"<<endl;
          }
      };
      
      class Bar : public Foo
      {
      public:
          int y;
      
          void printStuff()
          {
              cout<<"derived Bar printStuff called"<<endl;
          }
      };
      
      int main()
      {
      
          Foo *foo=new Foo;
          foo->printStuff();/////this call the base function
          foo=new Bar;
          foo->printStuff();
      }
      

      【讨论】:

        【解决方案6】:

        以防万一您为班级中的许多功能执行此操作:

        class Foo {
        public:
          virtual void f1() {
            // ...
          }
          virtual void f2() {
            // ...
          }
          //...
        };
        
        class Bar : public Foo {
        private:
          typedef Foo super;
        public:
          void f1() {
            super::f1();
          }
        };
        

        如果您想重命名 Foo,这可能会节省一些写作时间。

        【讨论】:

        • 有趣的方法,但不适用于多重继承。
        • 如果你有多个基类怎么办?无论如何,试图让 C++ 看起来像其他语言是愚蠢的,而且往往是徒劳的。只要记住基地的名称并以此命名即可。
        【解决方案7】:

        是的,

        class Bar : public Foo
        {
            ...
        
            void printStuff()
            {
                Foo::printStuff();
            }
        };
        

        它与 Java 中的 super 相同,只是它允许在多重继承时从不同的基中调用实现。

        class Foo {
        public:
            virtual void foo() {
                ...
            }
        };
        
        class Baz {
        public:
            virtual void foo() {
                ...
            }
        };
        
        class Bar : public Foo, public Baz {
        public:
            virtual void foo() {
                // Choose one, or even call both if you need to.
                Foo::foo();
                Baz::foo();
            }
        };
        

        【讨论】:

        • 这是一个比所选答案更好的答案。谢谢。
        • 多重继承?哎呀!
        【解决方案8】:

        C++ 语法是这样的:

        class Bar : public Foo {
          // ...
        
          void printStuff() {
            Foo::printStuff(); // calls base class' function
          }
        };
        

        【讨论】:

        • 这样做有什么问题吗?这是不好的做法吗?
        • @David:不,这样做是完全正常的,尽管它是否真的有用可能取决于你的实际课程。仅当您想要发生的事情时才调用基类方法;)。
        • 小心,当您的客户需要这样做时,这是一种代码气味! (称为call super requirement,在这里查看:en.wikipedia.org/wiki/Call_super
        • @v.oddou:在有用的时候调用超类并没有错。您链接的那个页面只是一些涉及继承的特定潜在问题。它甚至自称调用超类一般来说没有错:“请注意,调用父类的要求是反模式。在真正的代码,其中子类中的方法可能仍需要超类的功能,通常只是增强父类的功能。”
        • 这对大多数人来说可能很明显,但为了完整起见,请记住永远不要在构造函数和析构函数中这样做。
        猜你喜欢
        • 1970-01-01
        • 2016-02-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多