0

In fact, similar questions were asked here and there, but the answers were not satisfied. The code example is

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;
}

and the output is

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

CBase::act5()! 

With respect to p->act1()

  • since act1() is a virtual function and derived class doesn't implement it, the program will call CBase::act1();
  • then program will call CBase::act2();
  • act3() is a vritual function, the program will call CDervie::act3();
  • act4() is also a vritual function, the program will call CDervie::act4();
  • here comes the part that I don't understand, act5() is not a virtual function, and p is a pointer belonging to CBase, basically p can only access the function in CBase! But the output is CDerive::act5()!

In contrast, p->act5() will call CBase::act5() as I think.

There seems to be contradictory between the principle - the base class pointer can only access the function defined in base class and virtual function, and the real output. The reason can't be explained from virtual table either, since CDerive::act5() is not even in the virtual table. So, my questions are

  • what is the rationale behind those ?
  • what did happen when CBase *p=new CDerive or CDerive a; CBase *p=&a?
5
  • You call act5 from CDerive::act4... static type is so CDerive. Commented Apr 13, 2021 at 14:36
  • Possibly relevant (or even a duplicate?): stackoverflow.com/q/67065428/10871073 Commented Apr 13, 2021 at 14:43
  • In the first case, it is not p that accesses act5, but the CDerive instance itself, inside CDerive::act4. When you call a non-virtual member function from another member function, it is selected based on the static type of *this in that member function. Commented Apr 13, 2021 at 14:52
  • @AdrianMole what a coincidence, we both asked the same kind of question at the same time Commented Apr 14, 2021 at 6:05
  • @molbdnilo I think I understand what you mean. Thank you very much, and I will write down my understanding below. Commented Apr 14, 2021 at 6:08

2 Answers 2

0

I'm going to re-state your bullet points, and make small changes to them.

  • act1() is a non-abstract, public virtual function that exists in CBase and not CDerive, so p uses the implementation in CBase. Because it was implemented in CBase, it is not required to be overridden in a child class
  • Same as above for act2(). Note that act2() is non-virtual
  • 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`
  • Same as above for act4()
  • Because act4() above was called from a CDerive object, the call to act5() will also come from CDerive

New bullet:

  • Then in main, this line p->act5() calls the CBase version. This is because p is a CBase*, and act5() is not virtual. So it follows that it calls from CBase.
Sign up to request clarification or add additional context in comments.

Comments

0

I think the key point of this question is the understanding of this pointer. When compiling, the real function is (taking CDerive::act4() as an example)

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

So, this->act5() is equivalent to CDerive::act5() and the output is CDerive::act5()!.

But wait, this seems to be contradictory to the function CBase::act2(). Following the same logic, the real function should be

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

and this->act3() will call CBase::act3(), this is not right!

So, the real calling procedure is

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

using two examples above to demonstrate it :

  • act5() is not a virtual function, so the program will call CDerive::act5()
  • act3() is a virtual funciton, so the program will call CDerive::act3()

In this way, the logic is consistent and the output is right.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.