3
#include <iostream>

using namespace std;

class parent {
   public:
    virtual void f() { cout << "parent::f2()" << endl; }
};

class child : public parent {
   public:
    void f() { cout << "child::f()" << endl; }
};

class grandchild : public child {
   public:
    void f() { cout << "grandchild::f()" << endl; }
};

int main() {
    grandchild grandchild1;
    parent *p = &grandchild1;
    p->f();
}

The above code runs like this

grandchild::f()

In class child, I didn't define f() as a virtual function. Why does p->f() still call the f() in class grandchild?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
yingma
  • 31
  • 2
  • 5
    Once a function is declared `virtual`, it will be `virtual` in all derived classes. – Yksisarvinen Aug 17 '23 at 10:53
  • `p` is a pointer to `parent` and in `parent` the method is already decalred virtual. Its not possible for a derived class to remove the virtualness – 463035818_is_not_an_ai Aug 17 '23 at 10:55
  • Related questions: https://stackoverflow.com/questions/3167045/do-i-need-to-specify-virtual-on-the-sub-classes-methods-as-well https://stackoverflow.com/questions/4895294/c-virtual-keyword-for-functions-in-derived-classes-is-it-necessary https://stackoverflow.com/questions/4024476/should-methods-that-implement-pure-virtual-methods-of-an-interface-class-be-decla/ (unfortunately, all of them are old and suggest adding `virtual` for clean code instead of `override`...) – Yksisarvinen Aug 17 '23 at 10:57
  • @Yksisarvinen I sometimes wonder whats the right way to deal with such questions. One could retroactively retag them as `c++03` (or whatever is appropriate). On the other hand, sometimes the uptodate answer is somewhere down the list (https://stackoverflow.com/a/4026322/4117728), and such answers would get invalidated by retagging – 463035818_is_not_an_ai Aug 17 '23 at 11:07
  • also this one https://stackoverflow.com/a/26375213/4117728 hidden behind lots of older answers – 463035818_is_not_an_ai Aug 17 '23 at 11:09
  • Side note on design : Don't confuse biological inheritance with class inheritance. In fact you probably should have only one class `class Human`. And model Parent/Child as relations. Object oriented doesn't mean use inheritance for everything. – Pepijn Kramer Aug 17 '23 at 11:45
  • Another side note: About [`using namespace std`](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)… – Aconcagua Aug 17 '23 at 11:49
  • Yet another side note: If a function is virtual in the base class then a function with the same signature in a deriving class *overrides* it – it is a good idea to mark such functions with `override` keyword: It makes obvious what actually is going on, but more important it makes compilation fail on mismatching signatures (mixed up parameter types, forgotten parameters, ... – or because the base class interface changed). – Aconcagua Aug 17 '23 at 11:51
  • @PepjinKramer I wonder where the parent-child analogy for inheritance originates from. Imho it couldnt be more confusing for beginners. I mean, in what world there is a is-a relation between the child and its parent? A new born child is a parent!? – 463035818_is_not_an_ai Aug 17 '23 at 13:56

1 Answers1

4

According to the C++20 Standard (Virtual functions [class.virtual])

1 A non-static member function is a virtual function if it is first declared with the keyword virtual or if it overrides a virtual member function declared in a base class (see below).

and

2 If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list ([dcl.fct]), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf overrides113 Base::vf.

Pay attention to that even a virtual function can be hidden in some intermediate derived class nevertheless it can be overriden in a subsequent derived class.

That is if for example you will change your classes the following way

class parent
{
public:
    virtual void f() { cout << "parent::f()" << endl; }
};

class child : public parent
{
public:
    void f( int ) { cout << "child::f()" << endl; }
};

class grandchild : public child
{
public:
    void f() { cout << "grandchild::f()" << endl; }
};

where the function f( int ) declared in the class child hides the virtual function f() declared in the class parent nevertheless you can override it in the class grandchild. The program output wll be the same as in the original program.

Here is a demonstration program.

#include <iostream>

using namespace std;

class parent
{
public:
    virtual void f() { cout << "parent::f()" << endl; }
};

class child : public parent
{
public:
    void f( int = 0 ) { cout << "child::f( int )" << endl; }
};

class grandchild : public child
{
public:
    void f() { cout << "grandchild::f()" << endl; }
};

int main()
{
    grandchild grandchild1;
    parent *p = &grandchild1;
    p->f(); 

    child child1;
    p = &child1;
    p->f();
}

The program output is

grandchild::f()
parent::f()

Some remarks. For the class child the final overrider of the virtual function, declared in the class parent, is the function parent::f. For the class grandchild the final overrider is the function grandchild::f.

It is better to use always the specifier override in declarations of virtual functions as for example

class grandchild : public child
{
public:
    void f() override { cout << "grandchild::f()" << endl; }
};

In this case your code will be more clear.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335