Namensräume
Varianten
Aktionen

Abgeleitete Klassen

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Jeder Klassentyp (egal ob deklariert mit class-key class oder struct) kann als abgeleitet von einer oder mehreren Basisklassen deklariert werden, die ihrerseits von ihren eigenen Basisklassen abgeleitet sein können, wodurch eine Vererbungshierarchie entsteht.

Inhalt

[bearbeiten] Syntax

Die Liste der Basisklassen wird in der base-clause der Klassendeklarationssyntax angegeben. Die base-clause besteht aus dem Zeichen : gefolgt von einer durch Kommata getrennten Liste von einem oder mehreren base-specifiern.

attr (optional) class-or-computed (1)
attr (optional) virtual class-or-computed (2)
attr (optional) access-specifier class-or-computed (3)
attr (optional) virtual access-specifier class-or-computed (4)
attr (optional) access-specifier virtual class-or-computed (5)
1) Definiert eine nicht-virtuelle Vererbung mit standardmäßiger Member-Zugänglichkeit.
2) Definiert eine virtuelle Vererbung mit standardmäßiger Member-Zugänglichkeit.
3) Definiert eine nicht-virtuelle Vererbung mit gegebener Member-Zugänglichkeit.
4) Definiert eine virtuelle Vererbung mit gegebener Member-Zugänglichkeit.
5) Dasselbe wie 4), virtual und access-specifier können in beliebiger Reihenfolge erscheinen.
attr - (seit C++11) Sequenz einer beliebigen Anzahl von Attributen
access-specifier - einer von private, public oder protected
class-or-computed - eine von
  • nested-name-specifier (optional) type-name
  • nested-name-specifier template simple-template-id
(seit C++11)
(seit C++26)

Ein elaborierter Typspezifizierer kann aufgrund von Syntaxbeschränkungen nicht direkt als class-or-computed erscheinen.

base-specifiers in einer base-clause können Parameter Pack Expansions sein.

Eine Klasse oder Struktur, die final deklariert ist, kann nicht durch class-or-computed bezeichnet werden.

(seit C++11)

Wenn access-specifier weggelassen wird, ist er standardmäßig public für abgeleitete Klassen, die mit class-key struct deklariert werden, und private für abgeleitete Klassen, die mit class-key class deklariert werden.

struct Base
{
    int a, b, c;
};
 
// every object of type Derived includes Base as a subobject
struct Derived : Base
{
    int b;
};
 
// every object of type Derived2 includes Derived and Base as subobjects
struct Derived2 : Derived
{
    int c;
};

Klassen, die durch class-or-computed bezeichnet werden und in der base-clause aufgeführt sind, sind direkte Basisklassen. Ihre Basen sind indirekte Basisklassen. Dieselbe Klasse kann nicht mehr als einmal als direkte Basisklasse angegeben werden, aber dieselbe Klasse kann sowohl direkte als auch indirekte Basisklasse sein.

Jede direkte und indirekte Basisklasse ist als Basisklassen-Subobjekt innerhalb der Objekt-Repräsentation der abgeleiteten Klasse mit einem ABI-abhängigen Offset vorhanden. Leere Basisklassen erhöhen normalerweise nicht die Größe des abgeleiteten Objekts aufgrund der Empty Base Optimization. Die Konstruktoren von Basisklassen-Subobjekten werden vom Konstruktor der abgeleiteten Klasse aufgerufen: Argumente können diesen Konstruktoren in der Member-Initialisierungsliste übergeben werden.

[bearbeiten] Virtuelle Basisklassen

Für jede einzelne Basisklasse, die virtual angegeben ist, enthält das am weitesten abgeleitete Objekt nur ein Basisklassen-Subobjekt dieses Typs, auch wenn die Klasse mehrmals in der Vererbungshierarchie vorkommt (solange sie jedes Mal virtual geerbt wird).

struct B { int n; };
class X : public virtual B {};
class Y : virtual public B {};
class Z : public B {};
 
// every object of type AA has one X, one Y, one Z, and two B's:
// one that is the base of Z and one that is shared by X and Y
struct AA : X, Y, Z
{
    AA()
    {
        X::n = 1; // modifies the virtual B subobject's member
        Y::n = 2; // modifies the same virtual B subobject's member
        Z::n = 3; // modifies the non-virtual B subobject's member
 
        std::cout << X::n << Y::n << Z::n << '\n'; // prints 223
    }
};

Ein Beispiel für eine Vererbungshierarchie mit virtuellen Basisklassen ist die iostream-Hierarchie der Standardbibliothek: std::istream und std::ostream sind von std::ios abgeleitet, wobei virtuelle Vererbung verwendet wird. std::iostream ist von sowohl std::istream als auch std::ostream abgeleitet, so dass jede Instanz von std::iostream ein std::ostream-Subobjekt, ein std::istream-Subobjekt und nur ein std::ios-Subobjekt (und folglich ein std::ios_base) enthält.

Alle virtuellen Basis-Subobjekte werden vor allen nicht-virtuellen Basis-Subobjekten initialisiert, sodass nur die am weitesten abgeleitete Klasse die Konstruktoren der virtuellen Basen in ihrer Member-Initialisierungsliste aufruft.

struct B
{
    int n;
 
    B(int x) : n(x) {}
};
 
struct X : virtual B { X() : B(1) {} };
struct Y : virtual B { Y() : B(2) {} };
struct AA : X, Y     { AA() : B(3), X(), Y() {} };
 
// the default constructor of AA calls the default constructors of X and Y
// but those constructors do not call the constructor of B because B is a virtual base
AA a; // a.n == 3
 
// the default constructor of X calls the constructor of B
X x;  // x.n == 1

Es gibt spezielle Regeln für die nicht qualifizierte Namenssuche für Klassenmember, wenn virtuelle Vererbung beteiligt ist (manchmal als Dominanzregeln bezeichnet).

[bearbeiten] Öffentliche Vererbung

Wenn eine Klasse public als Member-Zugriffsspezifizierer verwendet, um von einer Basisklasse abzuleiten, sind alle öffentlichen Member der Basisklasse als öffentliche Member der abgeleiteten Klasse zugänglich, und alle geschützten Member der Basisklasse sind als geschützte Member der abgeleiteten Klasse zugänglich (private Member der Basis sind nie zugänglich, es sei denn, sie sind als Friend deklariert).

Öffentliche Vererbung modelliert die Subtyp-Beziehung der objektorientierten Programmierung: Das Objekt der abgeleiteten Klasse IST-EIN Objekt der Basisklasse. Referenzen und Zeiger auf ein abgeleitetes Objekt sollen von jedem Code, der Referenzen oder Zeiger auf eine seiner öffentlichen Basen erwartet, verwendbar sein (siehe LSP) oder, in DbC-Begriffen, eine abgeleitete Klasse sollte Klasseninvarianten ihrer öffentlichen Basen beibehalten, keine Vorbedingung stärken oder eine Nachbedingung einer Member-Funktion schwächen, die sie überschreibt.

#include <iostream>
#include <string>
#include <vector>
 
struct MenuOption { std::string title; };
 
// Menu is a vector of MenuOption: options can be inserted, removed, reordered...
// and has a title.
class Menu : public std::vector<MenuOption>
{
public:
    std::string title;
 
    void print() const
    {
        std::cout << title << ":\n";
        for (std::size_t i = 0, s = size(); i < s; ++i)
            std::cout << "  " << (i + 1) << ". " << at(i).title << '\n';
    }
};
// Note: Menu::title is not problematic because its role is independent of the base class.
 
enum class Color { WHITE, RED, BLUE, GREEN };
 
void apply_terminal_color(Color) { /* OS-specific */ }
 
// THIS IS BAD!
// ColorMenu is a Menu where every option has a custom color.
class ColorMenu : public Menu
{
public:
    std::vector<Color> colors;
 
    void print() const
    {
        std::cout << title << ":\n";
 
        for (std::size_t i = 0, s = size(); i < s; ++i)
        {
            std::cout << "  " << (i + 1) << ". ";
            apply_terminal_color(colors[i]);
            std::cout << at(i).title << '\n';
            apply_terminal_color(Color::WHITE);
        }
    }
};
// ColorMenu needs the following invariants that cannot be satisfied
// by publicly inheriting from Menu, for example:
// - ColorMenu::colors and Menu must have the same number of elements
// - To make sense, calling erase() should remove also elements from colors,
//   in order to let options keep their colors
// Basically every non-const call to a std::vector method will break the invariant
// of the ColorMenu and will need fixing from the user by correctly managing colors.
 
int main()
{
    ColorMenu color_menu;
 
    // The big problem of this class is that we must keep ColorMenu::Color
    // in sync with Menu.
    color_menu.push_back(MenuOption{"Some choice"});
 
    // color_menu.print(); // ERROR! colors[i] in print() is out of range
 
    color_menu.colors.push_back(Color::RED);
 
    color_menu.print(); // OK: colors and Menu has the same number of elements
}

[bearbeiten] Geschützte Vererbung

Wenn eine Klasse protected als Member-Zugriffsspezifizierer verwendet, um von einer Basisklasse abzuleiten, sind alle öffentlichen und geschützten Member der Basisklasse als geschützte Member der abgeleiteten Klasse zugänglich (private Member der Basis sind nie zugänglich, es sei denn, sie sind als Friend deklariert).

Geschützte Vererbung kann für "kontrollierte Polymorphie" verwendet werden: Innerhalb der Member von Derived, sowie innerhalb der Member aller weiteren abgeleiteten Klassen, IST Derived-EIN Basis: Referenzen und Zeiger auf Derived können dort verwendet werden, wo Referenzen und Zeiger auf Base erwartet werden.

[bearbeiten] Private Vererbung

Wenn eine Klasse private als Member-Zugriffsspezifizierer verwendet, um von einer Basisklasse abzuleiten, sind alle öffentlichen und geschützten Member der Basisklasse als private Member der abgeleiteten Klasse zugänglich (private Member der Basis sind nie zugänglich, es sei denn, sie sind als Friend deklariert).

Private Vererbung wird häufig im Policy-Based Design verwendet, da Policies normalerweise leere Klassen sind, und ihre Verwendung als Basis sowohl statische Polymorphie ermöglicht als auch die Empty Base Optimization nutzt.

Private Vererbung kann auch verwendet werden, um die Kompositionsbeziehung zu implementieren (das Basisklassen-Subobjekt ist ein Implementierungsdetail des abgeleiteten Klassenobjekts). Die Verwendung eines Members bietet eine bessere Kapselung und wird im Allgemeinen bevorzugt, es sei denn, die abgeleitete Klasse benötigt Zugriff auf geschützte Member (einschließlich Konstruktoren) der Basis, muss einen virtuellen Member der Basis überschreiben, benötigt, dass die Basis vor einigen anderen Basis-Subobjekten konstruiert und danach zerstört wird, muss eine virtuelle Basis teilen oder muss die Konstruktion einer virtuellen Basis steuern. Die Verwendung von Membern zur Implementierung von Komposition ist auch nicht anwendbar bei Mehrfachvererbung von einem Parameter Pack oder wenn die Identitäten der Basisklassen zur Kompilierzeit durch Template-Metaprogrammierung bestimmt werden.

Ähnlich wie bei der geschützten Vererbung kann auch die private Vererbung für kontrollierte Polymorphie verwendet werden: Innerhalb der Member der abgeleiteten Klasse (aber nicht innerhalb weiterer abgeleiteter Klassen) IST abgeleitet-EIN Basis.

template<typename Transport>
class service : private Transport // private inheritance from the Transport policy
{
public:
    void transmit()
    {
        this->send(...); // send using whatever transport was supplied
    }
};
 
// TCP transport policy
class tcp
{
public:
    void send(...);
};
 
// UDP transport policy
class udp
{
public:
    void send(...);
};
 
service<tcp> service(host, port); 
service.transmit(...); // send over TCP

[bearbeiten] Namenssuche für Member

Regeln für die nicht qualifizierte und qualifizierte Namenssuche für Klassenmember sind detailliert in Namenssuche.

[bearbeiten] Schlüsselwörter

virtual

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 1710 C++98 die Syntax von class-or-decltype machte es unmöglich, von
einer abhängigen Klasse abzuleiten, bei der der template-Disambiguator erforderlich ist
erlaubt template

[bearbeiten] Siehe auch