【问题标题】:C++ call a non virtual function from virtual function in derived classC ++从派生类中的虚函数调用非虚函数
【发布时间】:2021-04-13 14:32:20
【问题描述】:

其实herethere也问过类似的问题,但答案都不满意。代码示例是

class  CBase
{
public:
    virtual void act1(){cout<<"CBase::act1()! "<<endl;  act2();}
    void act2()        {cout<<"CBase::act2()! "<<endl;  act3();}
    virtual void act3(){cout<<"CBase::act3()! "<<endl;  act4();}
    virtual void act4(){cout<<"CBase::act4()! "<<endl;  act5();}
    void act5()        {cout<<"CBase::act5()! "<<endl;    }
    virtual ~CBase(){}
} ;

class  CDerive : public  CBase
{
public:
    void act3(){cout<<"CDerive::act3()! "<<endl; act4();}
    void act4(){cout<<"CDerive::act4()! "<<endl; act5();}    
    void act5(){cout<<"CDerive::act5()! "<<endl;        }
    virtual ~CDerive(){}
} ;
int main()
{
    CBase *p=new CDerive;
    p->act1();
    cout<<endl;
    p->act5();
    delete p;
    return 0;
}

输出是

CBase::act1()! 
CBase::act2()! 
CDerive::act3()! 
CDerive::act4()! 
CDerive::act5()! 

CBase::act5()! 

关于p-&gt;act1()

  • 由于act1()是一个虚函数,派生类没有实现它,所以程序会调用CBase::act1()
  • 然后程序会调用CBase::act2()
  • act3()是虚拟函数,程序会调用CDervie::act3()
  • act4()也是虚拟函数,程序会调用CDervie::act4()
  • 来了我不明白的部分,act5()不是虚函数,而p是属于CBase的指针,基本上p只能访问CBase中的函数!但输出是CDerive::act5()!

相比之下,p-&gt;act5() 会像我想的那样调用CBase::act5()

原理——基类指针只能访问基类和虚函数中定义的函数,与真正的输出之间似乎存在矛盾。原因也无法从虚拟表中解释,因为CDerive::act5() 甚至不在虚拟表中。所以,我的问题是

  • 这些背后的基本原理是什么?
  • CBase *p=new CDeriveCDerive a; CBase *p=&amp;a 时发生了什么?

【问题讨论】:

  • 你从CDerive::act4调用act5...静态类型就是CDerive
  • 可能相关(甚至重复?):stackoverflow.com/q/67065428/10871073
  • 在第一种情况下,访问act5的不是p,而是CDerive实例本身,在CDerive::act4内部。当你从另一个成员函数中调用一个非虚拟成员函数时,它是根据该成员函数中*this的静态类型来选择的。
  • @AdrianMole 真巧,我们俩同时问了同样的问题
  • @molbdnilo 我想我明白你的意思。非常感谢,我会在下面写下我的理解。

标签: c++ polymorphism


【解决方案1】:

我将重新陈述您的要点,并对它们进行一些小改动。

  • act1() 是一个非抽象的公共虚函数,存在于CBase CDerive,所以p 使用CBase 中的实现。因为是在CBase中实现的,所以不需要在子类中重写
  • 与上述act2() 相同。请注意,act2() 是非虚拟的
  • act3() is a virtual function in CBase, and is overridden in CDerive. Since pis allocated as aCDerive, the override is used and act3()is called fromCDerive`
  • 同上act4()
  • 因为上面的act4() 是从CDerive 对象调用的,所以对act5() 的调用也将来自CDerive

新项目符号:

  • 然后在 main 中,这行 p-&gt;act5() 调用 CBase 版本。这是因为pCBase*,而act5() 不是虚拟的。因此,它从CBase 调用。

【讨论】:

    【解决方案2】:

    我觉得这个问题的重点是对this指针的理解。编译时,真正的函数是(以CDerive::act4()为例)

    void CDerive::act4(CDerive *this)
    {
        cout<<"CDerive::act4()! "<<endl; 
        this->act5();
    }
    

    所以,this-&gt;act5() 等价于CDerive::act5(),输出为CDerive::act5()!

    但是等等,这似乎与函数CBase::act2() 相矛盾。按照同样的逻辑,真正的函数应该是

    void CBase::act2(CBase *this)        
    {
        cout<<"CBase::act2()! "<<endl;  
        this->act3();
    }
    

    this-&gt;act3() 会调用CBase::act3(),这是不对的!

    所以,真正的调用过程是

    if func() is not a virtual function 
        this -> func() = class::func() 
    else
        this -> func() = v_ptr -> v_talbe -> func()
    

    用上面的两个例子来演示:

    • act5()不是虚函数,所以程序会调用CDerive::act5()
    • act3()是一个虚函数,所以程序会调用CDerive::act3()

    这样逻辑一致,输出正确。

    【讨论】:

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