C++ "virtual" keyword for functions in derived classes. Is it necessary?
Asked Answered
C

9

268

With the struct definition given below...

struct A {
    virtual void hello() = 0;
};

Approach #1:

struct B : public A {
    virtual void hello() { ... }
};

Approach #2:

struct B : public A {
    void hello() { ... }
};

Is there any difference between these two ways to override the hello function?

Circularize answered 4/2, 2011 at 6:46 Comment(2)
In C++11 you can write "void hello() override {}" to explicitly declare that you're overriding a virtual method. The compiler will fail if a base virtual method does not exist, and it has the same readability as placing "virtual" on the descendant class.Rizal
Actually, in gcc's C++11, writing void hello() override {} in the derived class is fine because the base class has specified that the method hello() is virtual. In other words, the use of the word virtual in the derived class is not necessary/mandatory, for gcc/g++ anyway. (I'm using gcc version 4.9.2 on a RPi 3) But it's good practice to include the keyword virtual in the derived class's method anyway.Nalley
Z
237

They are exactly the same. There is no difference between them other than that the first approach requires more typing and is potentially clearer.

Zachariahzacharias answered 4/2, 2011 at 6:47 Comment(9)
This is true, but the Mozilla C++ Portability Guide recommends always using virtual because "some compilers" issue warnings if you don't. Too bad they don't mention any examples of such compilers.Kaden
I would also add that explicitly marking it as virtual will help remind you to make the destructor virtual as well.Scrofula
Only to mention, same applicable to virtual destructorBonniebonns
@SergeyTachenov according to clifford's comment to his own answer, example of such compilers is armcc.Matteroffact
@Rasmi, the new portability guide is here, but now it recommends to use the override keyword.Kaden
The Mozilla C++ portability guide no longer recommends using virtual on derived class methods, it only recommends override.Hainaut
I would add also that the Approach #1 is not error prone to diamond inheritance, while the Approach #2 isSolanum
and another new guide is here developer.mozilla.org/en-US/docs/Mozilla/…Lait
What if I separate it into .h and .cpp files? Should I use virtual in both places?Cartesian
C
98

The 'virtualness' of a function is propagated implicitly, however at least one compiler I use will generate a warning if the virtual keyword is not used explicitly, so you may want to use it if only to keep the compiler quiet.

From a purely stylistic point-of-view, including the virtual keyword clearly 'advertises' the fact to the user that the function is virtual. This will be important to anyone further sub-classing B without having to check A's definition. For deep class hierarchies, this becomes especially important.

Caxton answered 4/2, 2011 at 7:0 Comment(3)
@James: armcc (ARM's compiler for ARM devices)Caxton
Overriding function in derived class is by convention implicitly virtual in C++, C#. Thus the keyword virtual should not be necessary. It is quite clear even in deep class hierarchies.Tittivate
@SecondPersonShooter : Is that not exactly what this answer already says. What point are you making that is not already stated? Clear to the compiler of course, but not clear to the human reader, which is also made clear.Caxton
D
74

The virtual keyword is not necessary in the derived class. Here's the supporting documentation, from the C++ Draft Standard (N3337) (emphasis mine):

10.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 (8.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 overrides Base::vf.

Danica answered 20/8, 2014 at 21:1 Comment(1)
This is by far the best answer here.Thoroughfare
P
43

No, the virtual keyword on derived classes' virtual function overrides is not required. But it is worth mentioning a related pitfall: a failure to override a virtual function.

The failure to override occurs if you intend to override a virtual function in a derived class, but make an error in the signature so that it declares a new and different virtual function. This function may be an overload of the base class function, or it might differ in name. Whether or not you use the virtual keyword in the derived class function declaration, the compiler would not be able to tell that you intended to override a function from a base class.

This pitfall is, however, thankfully addressed by the C++11 explicit override language feature, which allows the source code to clearly specify that a member function is intended to override a base class function:

struct Base {
    virtual void some_func(float);
};

struct Derived : Base {
    virtual void some_func(int) override; // ill-formed - doesn't override a base class method
};

The compiler will issue a compile-time error and the programming error will be immediately obvious (perhaps the function in Derived should have taken a float as the argument).

Refer to WP:C++11.

Priesthood answered 15/10, 2014 at 5:39 Comment(0)
N
14

Adding the "virtual" keyword is good practice as it improves readability , but it is not necessary. Functions declared virtual in the base class, and having the same signature in the derived classes are considered "virtual" by default.

Nasa answered 4/2, 2011 at 7:38 Comment(0)
E
8

There is no difference for the compiler, when you write the virtual in the derived class or omit it.

But you need to look at the base class to get this information. Therfore I would recommend to add the virtual keyword also in the derived class, if you want to show to the human that this function is virtual.

Eberhardt answered 4/2, 2011 at 6:57 Comment(0)
C
4

The virtual keyword should be added to functions of a base class to make them overridable. In your example, struct A is the base class. virtual means nothing for using those functions in a derived class. However, it you want your derived class to also be a base class itself, and you want that function to be overridable, then you would have to put the virtual there.

struct B : public A {
    virtual void hello() { ... }
};

struct C : public B {
    void hello() { ... }
};

Here C inherits from B, so B is not the base class (it is also a derived class), and C is the derived class. The inheritance diagram looks like this:

A
^
|
B
^
|
C

So you should put the virtual in front of functions inside of potential base classes which may have children. virtual allows your children to override your functions. There is nothing wrong with putting the virtual in front of functions inside of the derived classes, but it is not required. It is recommended though, because if someone would want to inherit from your derived class, they would not be pleased that the method overriding doesn't work as expected.

So put virtual in front of functions in all classes involved in inheritance, unless you know for sure that the class will not have any children who would need to override the functions of the base class. It is good practice.

Coppersmith answered 8/12, 2018 at 4:41 Comment(0)
A
3

There's a considerable difference when you have templates and start taking base class(es) as template parameter(s):

struct None {};

template<typename... Interfaces>
struct B : public Interfaces
{
    void hello() { ... }
};

struct A {
    virtual void hello() = 0;
};

template<typename... Interfaces>
void t_hello(const B<Interfaces...>& b) // different code generated for each set of interfaces (a vtable-based clever compiler might reduce this to 2); both t_hello and b.hello() might be inlined properly
{
    b.hello();   // indirect, non-virtual call
}

void hello(const A& a)
{
    a.hello();   // Indirect virtual call, inlining is impossible in general
}

int main()
{
    B<None>  b;         // Ok, no vtable generated, empty base class optimization works, sizeof(b) == 1 usually
    B<None>* pb = &b;
    B<None>& rb = b;

    b.hello();          // direct call
    pb->hello();        // pb-relative non-virtual call (1 redirection)
    rb->hello();        // non-virtual call (1 redirection unless optimized out)
    t_hello(b);         // works as expected, one redirection
    // hello(b);        // compile-time error


    B<A>     ba;        // Ok, vtable generated, sizeof(b) >= sizeof(void*)
    B<None>* pba = &ba;
    B<None>& rba = ba;

    ba.hello();         // still can be a direct call, exact type of ba is deducible
    pba->hello();       // pba-relative virtual call (usually 3 redirections)
    rba->hello();       // rba-relative virtual call (usually 3 redirections unless optimized out to 2)
    //t_hello(b);       // compile-time error (unless you add support for const A& in t_hello as well)
    hello(ba);
}

The fun part of it is that you can now define interface and non-interface functions later to defining classes. That is useful for interworking interfaces between libraries (don't rely on this as a standard design process of a single library). It costs you nothing to allow this for all of your classes - you might even typedef B to something if you'd like.

Note that, if you do this, you might want to declare copy / move constructors as templates, too: allowing to construct from different interfaces allows you to 'cast' between different B<> types.

It's questionable whether you should add support for const A& in t_hello(). The usual reason for this rewrite is to move away from inheritance-based specialization to template-based one, mostly for performance reasons. If you continue to support the old interface, you can hardly detect (or deter from) old usage.

Angiology answered 28/7, 2016 at 9:19 Comment(0)
I
0

I will certainly include the Virtual keyword for the child class, because

  • i. Readability.
  • ii. This child class my be derived further down, you don't want the constructor of the further derived class to call this virtual function.
Invite answered 16/3, 2014 at 15:11 Comment(1)
I think he means that without marking the child function as virtual, a programmer who derives from the child class later on may not realize that the function actually is virtual (because he never looked at the base class) and may potentially call it during construction (which may or may not do the right thing).Chestnut

© 2022 - 2024 — McMap. All rights reserved.