Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion with virtual keyword in C++

I was studying on effects of virtual keyword in C++ and I came up with this code.

#include<iostream>

using namespace std;

class A {
public:
    virtual void show(){
        cout << "A \n";
    }
};

class B : public A {
public:
    void show(){
        cout << "B \n";
    }
};

class C : public B {
public: 
    void show(){
        cout << "C \n"; 
    }
};

int main(){

    A *ab = new B;
    A *ac = new C;
    B *bc = new C;
    ab->show();
    ac->show();
    bc->show();

}

The expected output is:

B
C
B

Since show function in B is non-virtual. But the outcome when you compile this is:

B
C
C

It behaves as if show function in B is virtual. Why is this the case? Does B class gets overridden here? How come I point to the A class if I'm pointing a C class to the B class?

like image 338
user4709059 Avatar asked Nov 09 '17 12:11

user4709059


1 Answers

According to the C++ 2017 Standard (10.1.2 Function specifiers)

2 The virtual specifier shall be used only in the initial declaration of a non-static class member function; see 13.3.

And (13.3 Virtual functions)

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 (11.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides111 Base::vf. For convenience we say that any virtual function overrides itself. A virtual member function C::vf of a class object S is a final overrider unless the most derived class (4.5) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.

Thus the function show in the class B is a virtual function because it has the same signature as the function declared in the class A.

Consider a more interesting example when in the class B there is added the qualifier const to the member function show.

#include<iostream>

using namespace std;

class A {
public:
    virtual void show(){
        cout << "A \n";
    }
};

class B : public A {
public:
    void show() const{
        cout << "B \n";
    }
};

class C : public B {
public: 
    void show() {
        cout << "C \n"; 
    }
};

int main(){

    A *ab = new B;
    A *ac = new C;
    B *bc = new C;
    ab->show();
    ac->show();
    bc->show();

}

In this case the output will look like

A 
C 
B 

In this expression statement

    ab->show();

there is called the virtual function show declared in the class A.

In this statement

    ac->show();

there is called the same virtual function that is overridden in the class C. The compier uses the virtual function declaration in the class A because the static type of the pointer ac is A *.

In this statement

    bc->show();

there is called non-virtual member function show with the qualifier const because the static type of the pointer bc is B * and the compiler finds the function in the class B that hides the virtual function declared in the class A..

For the original program you could use the specifier override to make the class definitions more clear. For example

#include<iostream>

using namespace std;

class A {
public:
    virtual void show(){
        cout << "A \n";
    }
};

class B : public A {
public:
    void show() override{
        cout << "B \n";
    }
};

class C : public B {
public: 
    void show() override{
        cout << "C \n"; 
    }
};

int main(){

    A *ab = new B;
    A *ac = new C;
    B *bc = new C;
    ab->show();
    ac->show();
    bc->show();

}
like image 53
Vlad from Moscow Avatar answered Sep 21 '22 23:09

Vlad from Moscow