【问题标题】:virtual function call from base class从基类调用虚函数
【发布时间】:2010-09-28 16:26:08
【问题描述】:

假设我们有:


Class Base
{   
    virtual void f(){g();};
    virtual void g(){//Do some Base related code;}
};

Class Derived : public Base
{   
    virtual void f(){Base::f();};
    virtual void g(){//Do some Derived related code};
};

int main()
{
    Base *pBase = new Derived;
    pBase->f();
    return 0;  
}

将从Base::f() 调用哪个g()Base::g()Derived::g()?

谢谢...

【问题讨论】:

  • 请使用带有 1/0'ers 的按钮来很好地格式化您的代码。 (我编辑了它,但 OP 回滚了它。所以我自己不会再编辑它)
  • 请注意您提供了一个内存泄漏示例。您在 main 中有一个忘记删除。

标签: c++ polymorphism virtual


【解决方案1】:

将调用派生类的g。如果要调用base中的函数,调用

Base::g();

相反。如果您想调用派生版本,但仍希望调用基础版本,请安排 g 的派生版本在其第一条语句中调用基础版本:

virtual void g() {
    Base::g();
    // some work related to derived
}

模板方法设计模式中使用了来自基类的函数可以调用虚方法并将控制权转移到派生类的事实。对于 C++,它更好地称为Non-Virtual-Interface。它在 C++ 标准库中也被广泛使用(例如,C++ 流缓冲区有函数pub... 调用执行实际工作的虚函数。例如pubseekoff 调用受保护的seekoff)。我在这个答案中写了一个例子:How do you validate an object’s internal state?

【讨论】:

  • 有趣的是,我发现了 GCC 4.8.2 的一个问题:Base *pBase = (Base*)(void*)new Derived; 试图从我的 Base 类中调用我的纯虚函数。
  • 如果在构造函数中调用,则会调用g()基类,因为现在没有构造派生类。
【解决方案2】:

我认为你想发明Template Method Pattern

【讨论】:

    【解决方案3】:

    由于您已将 g() 定义为虚拟,因此无论您的代码当前访问的类型如何,都会在类的 vtable 中查找最派生的 g() 并调用它。

    请参阅C++ FAQ on virtual functions

    【讨论】:

      【解决方案4】:

      嗯...我不确定这应该编译。以下,

      Base *pBase = new Derived;
      

      是无效的,除非你有:

      Class Derived : public Base
      

      你的意思是想要吗?如果这是你的意思,

      pBase->f();
      

      那么调用栈会是这样的:

      Derived::f()
          Base::f()
              Derived::g()
      

      【讨论】:

        【解决方案5】:

        实际运行您的代码表明调用了 Derived::g()。

        【讨论】:

          【解决方案6】:

          pBase 是一个指向基址的指针。 pBase = new Derived 返回一个指向 Derived - Derived is-a Base 的指针。

          所以 pBase = new Derived 是有效的。

          pBase 引用一个 Base,因此它会将 Derived 视为 Base。

          pBase->f() 会调用 Derive::f();

          然后我们在代码中看到:

          Derive::f() --> Base::f() --> g() - 但是哪个 g??

          好吧,它调用 Derive::g() 因为那是 pBase “指向”的 g。

          答案:导出::g()

          【讨论】:

            【解决方案7】:

            它是 Derived::g,除非你在 Base 的构造函数中调用 g。因为在构造 Derived 对象之前调用了 Base 构造函数,所以在逻辑上无法调用 Derived::g,因为它可能会操作尚未构造的变量,因此会调用 Base::g。

            【讨论】:

            • 很好地说明了构造函数中发生的事情。
              Scott Meyers 说 link
            【解决方案8】:

            派生类的方法将被调用。

            这是因为在具有虚函数的类和覆盖这些函数的类中包含了虚表。 (这也称为动态调度。) 下面是实际情况:为Base 创建一个vtable,为Derived 创建一个vtable,因为每个类只有一个vtable。因为pBase 正在调用一个虚函数并被覆盖,所以调用了指向Derived 的vtable 的指针。称它为d_ptr,也称为vpointer:

            int main()
            {
                Base *pBase = new Derived;
                pBase->d_ptr->f();
                return 0;  
            }
            

            现在 d_ptr 调用 Derived::f(),它调用 Base::f(),然后查看 vtable 以查看要使用的 g()。因为vpointer只知道Derived中的g(),所以我们用的就是那个。因此,Derived::g() 被调用。

            【讨论】:

              【解决方案9】:

              如果在成员函数中,将调用派生类的g()。

              如果在构造函数或析构函数中,将调用基类的g()。

              https://www.geeksforgeeks.org/calling-virtual-methods-in-constructordestructor-in-cpp/

              // calling virtual methods in constructor/destructor
              #include<iostream> 
              using namespace std; 
              
              class dog 
              { 
              public: 
                  dog()  
                  { 
                      cout<< "Constructor called" <<endl; 
                      bark() ; 
                  } 
              
                  ~dog() 
                  {  
                      bark();  
                  } 
              
                  virtual void bark() 
                  {  
                      cout<< "Virtual method called" <<endl;  
                  } 
              
                  void seeCat()  
                  {  
                      bark();  
                  } 
              }; 
              
              class Yellowdog : public dog 
              { 
              public: 
                      Yellowdog()  
                      { 
                          cout<< "Derived class Constructor called" <<endl;  
                      } 
                      void bark()  
                      { 
                          cout<< "Derived class Virtual method called" <<endl;  
                      } 
              }; 
              
              int main() 
              { 
                  Yellowdog d; 
                  d.seeCat(); 
              } 
              

              输出:

              Constructor called
              Virtual method called
              Derived class Constructor called
              Derived class Virtual method called
              Virtual method called
              

              【讨论】:

                猜你喜欢
                • 2012-01-27
                • 1970-01-01
                • 1970-01-01
                • 2011-09-27
                • 2015-11-04
                • 2012-04-01
                • 2019-04-19
                相关资源
                最近更新 更多