Virtual method

from Wikipedia, the free encyclopedia

In object-oriented programming, a virtual method is a method of a class whose entry address is only determined at runtime. This so-called dynamic linking enables classes to be derived from a superclass and functions to be overwritten or overloaded . The concept of virtual methods is implemented by a compiler (translator) using function tables, for example .

In some programming languages ​​such as Java , Smalltalk and Python , all methods are virtual. On the other hand, in languages ​​like C ++ , C # , SystemVerilog or Object Pascal, methods must be virtualmarked with the keyword for this purpose , which offers the additional possibility of preventing overloading in subclasses.

Deriving classes and overriding methods

In object-oriented programming languages ​​such as C ++, C #, Object Pascal or Java, classes can be created by deriving them from other classes. Derived classes have all the methods and data fields of the original class and can be expanded with additional fields and methods. However, in some cases it is desirable to modify existing methods; i.e. to rewrite them. In this case one speaks of overwriting .

The so-called polymorphism also results from deriving classes . Each class represents its own data type . Derived classes have at least one additional data type, namely that of the base class (also referred to as the parent , super or parent class ). This makes it possible, for example, to use a list of objects of class A, although objects of class B (derived from A) are actually also stored in the list.

Problems for the translator

During compilation, a compiler tries to define an address in memory for each function called at which a function or method begins. In the later program, the CPU will jump to the corresponding address when it is called and continue working (some administrative work is also necessary, which is of no further importance here). For derived classes with overridden or overloaded methods is not always the compile time know which method to call. In the example with the list (see below), for example, the translator cannot always know when objects other than objects of type A appear in the list.

Solution: Indirect addressing

One solution is indirect addressing via a table. If the translator cannot determine which method to jump to, an entry address is not specified, but only a reference to an entry in the function table. This contains the specific entry addresses that are to be jumped to during the program run.

example

A list contains elements of type A and B. A is the superclass of B and B overwrites method m from A. Now method m is to be called for each element. For each class there is therefore a table with the addresses of functions. The recorded addresses of the table of objects of type B are different from those of the table of objects of type A. In the machine code, the CPU is now instructed to call the function that is located at table position "m" of the current object.

Abstract, virtual methods

Virtual methods can also be abstract . In the class in which the method is declared, the method remains empty, but can theoretically still be called. The abstract method is only overwritten in a derived class and can then be used.

When a class contains one or more abstract methods, it is called an abstract class . In C ++ and Java it is not possible to create an object of an abstract class. Object Pascal allows this, but an exception is thrown when an abstract method is called .

Purely virtual methods

Purely virtual methods ( pure virtual functions ) expand the concept of the abstract method even further. Since an abstract, virtual method can theoretically still be called, the methods are explicitly set to zero in C ++, for example . As a result, these methods can no longer be called, and no object can be created by the class. Derived classes must first implement these methods; only then can an object be created from them.

example

#include <iostream>

using namespace std;

struct Tier {
    // Rein virtuelle Methode
    virtual void essen() = 0;
};

struct Wolf: Tier {
    // Implementierung der virtuellen Methode
    void essen() {
        cout << "Wölfe können essen!" << endl;
    }
};

int main() {
    Wolf wolf1;
    wolf1.essen();
}

Virtual destructors

Another peculiarity of C ++ are destructors , which are used for final tasks like freeing memory . Any class whose attributes are not primitive types or which uses other resources (such as a database connection) should definitely release them in its destructor. In order to always be able to access the correct destructor, the destructor of the ancestor must be virtualdeclared as.

The following example shows the use and inheritance of non-virtual destructors, which leads to undefined behavior.

#include <iostream>
#include <memory>

using namespace std;

struct A {
    A() {}
    ~A() {
        cout << "Zerstöre A" << endl;
    }
};

struct B: A {
    B() {}
    ~B() {
        cout << "Zerstöre B" << endl;
    }
};

int main() {
    // Gemäß C++-Standard undefiniertes Verhalten
    // Meist wird am Ende nur ~A() aufgerufen, da ~A() nicht virtuell ist
    unique_ptr<A> b1 = make_unique<B>();

    // Am Ende werden Destruktoren ~B() und ~A() aufgerufen
    unique_ptr<B> b2 = make_unique<B>();
}

One possible output would be:

Zerstöre B
Zerstöre A
Zerstöre A