Virtual functions (C++ only)
By default, C++ matches a function call with the correct
function definition at compile time. This is called static binding.
You can specify that the compiler match a function call with the correct
function definition at run time; this is called dynamic binding.
You declare a function with the keyword virtual
if
you want the compiler to use dynamic binding for that specific function.
#include <iostream>
using namespace std;
struct A {
void f() { cout << "Class A" << endl; }
};
struct B: A {
void f() { cout << "Class B" << endl; }
};
void g(A& arg) {
arg.f();
}
int main() {
B x;
g(x);
}
The following is the output of the above example: Class A
When
function g()
is called, function A::f()
is
called, although the argument refers to an object of type B
.
At compile time, the compiler knows only that the argument of function g()
is an lvalue reference to an object derived from A
;
it cannot determine whether the argument is an lvalue
reference to an object of type A
or type B
.
However, this can be determined at run time. The following example
is the same as the previous example, except that A::f()
is
declared with the virtual
keyword: #include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "Class A" << endl; }
};
struct B: A {
void f() { cout << "Class B" << endl; }
};
void g(A& arg) {
arg.f();
}
int main() {
B x;
g(x);
}
The following is the output of the above example: Class B
The virtual
keyword
indicates to the compiler that it should choose the appropriate definition
of f()
not by the type of lvalue reference,
but by the type of object that the lvalue reference refers
to.Therefore, a virtual function is a member function you may redefine for other derived classes, and can ensure that the compiler will call the redefined virtual function for an object of the corresponding derived class, even if you call that function with a pointer or reference to a base class of the object.
A class that declares or inherits a virtual function is called a polymorphic class.
f
in a class A
, and you derive
directly or indirectly from A
a class named B
.
If you declare a function named f
in class B
with
the same name and same parameter list as A::f
, then B::f
is
also virtual (regardless whether or not you declare B::f
with
the virtual
keyword) and it overrides A::f
.
However, if the parameter lists of A::f
and B::f
are
different, A::f
and B::f
are considered
different, B::f
does not override A::f
,
and B::f
is not virtual (unless you have declared
it with the virtual
keyword). Instead B::f
hides A::f
.
The following example demonstrates this: #include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "Class A" << endl; }
};
struct B: A {
virtual void f(int) { cout << "Class B" << endl; }
};
struct C: B {
void f() { cout << "Class C" << endl; }
};
int main() {
B b; C c;
A* pa1 = &b;
A* pa2 = &c;
// b.f();
pa1->f();
pa2->f();
}
The following is the output of the above
example: Class A
Class C
The function B::f
is not virtual.
It hides A::f
. Thus the compiler will not allow the
function call b.f()
. The function C::f
is
virtual; it overrides A::f
even though A::f
is
not visible in C
.If you declare a base class destructor as virtual, a derived class destructor will override that base class destructor, even though destructors are not inherited.
B::f
overrides the virtual function A::f
.
The return types of A::f
and B::f
may
differ if all the following conditions are met: - The function
B::f
returns a pointer or a reference to a class of typeT
, andA::f
returns a pointer or a reference to an unambiguous direct or indirect base class ofT
. - The const or volatile qualification of the pointer or reference returned
by
B::f
has the same or less const or volatile qualification of the pointer or reference returned byA::f
. - The return type of
B::f
must be complete at the point of declaration ofB::f
, or it can be of typeB
. A::f
returns an lvalue reference if and only ifB::f
returns an lvalue reference.
#include <iostream>
using namespace std;
struct A { };
class B : private A {
friend class D;
friend class F;
};
A global_A;
B global_B;
struct C {
virtual A* f() {
cout << "A* C::f()" << endl;
return &global_A;
}
};
struct D : C {
B* f() {
cout << "B* D::f()" << endl;
return &global_B;
}
};
struct E;
struct F : C {
// Error:
// E is incomplete
// E* f();
};
struct G : C {
// Error:
// A is an inaccessible base class of B
// B* f();
};
int main() {
D d;
C* cp = &d;
D* dp = &d;
A* ap = cp->f();
B* bp = dp->f();
};
The following is the output of the above example: B* D::f()
B* D::f()
The statement A* ap = cp->f()
calls D::f()
and
converts the pointer returned to type A*
. The statement B*
bp = dp->f()
calls D::f()
as well but
does not convert the pointer returned; the type returned is B*
.
The compiler would not allow the declaration of the virtual function F::f()
because E
is
not a complete class. The compiler would not allow the declaration
of the virtual function G::f()
because class A
is
not an accessible base class of B
(unlike friend
classes D
and F
, the definition
of B
does not give access to its members for class G
).A virtual function cannot be global or static because, by definition, a virtual function is a member function of a base class and relies on a specific object to determine which implementation of the function is called. You can declare a virtual function to be a friend of another class.
If a function is declared virtual in its base class, you
can still access it directly using the scope resolution (::
)
operator. In this case, the virtual function call mechanism is suppressed
and the function implementation defined in the base class is used.
In addition, if you do not override a virtual member function in a
derived class, a call to that function uses the function implementation
defined in the base class.
A function that has a deleted definition cannot override a function that does not have a deleted definition. Likewise, a function that does not have a deleted definition cannot override a function with a deleted definition.
- Defined
- Declared pure
- Defined and declared pure