Virtual insanity...

Virtual functions are used to achieve runtime polymorphism https://www.educba.com/virtual-keyword-in-c-plus-plus/

Uses of the virtual keyword

See https://en.cppreference.com/w/cpp/keyword/virtual

Related keywords: override, final

virtual function specifier

The virtual specifier specifies that a non-static member function is virtual and supports dynamic dispatch. It may only appear in the decl-specifier-seq of the initial declaration of a non-static member function (i.e., when it is declared in the class definition).

Virtual functions are member functions whose behavior can be overridden in derived classes. As opposed to non-virtual functions, the overriding behavior is preserved even if there is no compile-time information about the actual type of the class. That is to say, if a derived class is handled using pointer or reference to the base class, a call to an overridden virtual function would invoke the behavior defined in the derived class. Such a function call is known as virtual function call or virtual call. Virtual function call is suppressed if the function is selected using qualified name lookup

Virtual base class specifier

  • when to declare a virtual destructor
  • virtual function dispatch
  • Virtual base class

Cannot be declared virtual

Non-member functions and static member functions cannot be virtual. Function templates cannot be declared virtual. This applies only to functions that are themselves templates - a regular member function of a class template can be declared virtual.

Virtual tables

A non-virtual class has a size of 1 because in C++ classes can’t have zero size.

A virtual class has a size of 8 on a 64-bit machine because there’s a hidden pointer inside it pointing to a vtable. vtables are static translation tables, created for each virtual-class.

#include <iostream>

int main() {

struct A {
} a;

struct B {
virtual void func(){;}
} b;


std::cout << (sizeof a) << "n";
std::cout << (sizeof b) << "n";
}
Program returned: 0
1
8

https://godbolt.org/z/Wen6EP9Wx

Virtual base classes

The accepted wisdom is that the base class destructor must be declared virtual if deleting a derived class via a base class pointer is not to leak. In a hierachy of classes you can get away with only declaring virtual in the class instance that you call delete on. However, getting it slightly wrong can cause a crash so just put it in the base class.

#include <iostream>
#include <memory>

struct A {
~A(){ std::cout << "~An"; }
int i;
int j;
};

struct B : A {
~B(){ std::cout << "~Bn"; }
};

struct C : B {
virtual ~C(){ std::cout << "~Cn"; }
};

struct D : C {
~D(){ std::cout << "~Dn"; }
};

int main() {
std::unique_ptr<C> a = std::make_unique<D>();
}

https://godbolt.org/z/Gnee41v4M

vtables

int main() {
struct A {
virtual void blah(){;}
} a;
}

https://godbolt.org/z/5G9zcd3KP

Assembly

Note the vtable section. Adding the virtual keyword generates a lot more code.

main: # @main
push rbp
mov rbp, rsp
sub rsp, 16
lea rdi, [rbp - 8]
call main::A::A() [base object constructor]
xor eax, eax
add rsp, 16
pop rbp
ret
main::A::A() [base object constructor]: # @main::A::A() [base object constructor]
push rbp
mov rbp, rsp
mov qword ptr [rbp - 8], rdi
mov rax, qword ptr [rbp - 8]
movabs rcx, offset vtable for main::A
add rcx, 16
mov qword ptr [rax], rcx
pop rbp
ret
main::A::blah(): # @main::A::blah()
push rbp
mov rbp, rsp
mov qword ptr [rbp - 8], rdi
pop rbp
ret
vtable for main::A:
.quad 0
.quad typeinfo for main::A
.quad main::A::blah()

typeinfo name for main::A:
.asciz "Z4mainE1A"

typeinfo for main::A:
.quad vtable for __cxxabiv1::__class_type_info+16
.quad typeinfo name for main::A

results matching ""

    No results matching ""