Namespaces
Variants

Derived classes

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

Inhaltsverzeichnis

Syntax

Die Liste der Basisklassen wird in der base-clause der Klassendeklarationssyntax bereitgestellt. Die base-clause besteht aus dem Zeichen : gefolgt von einer durch Kommas getrennten Liste von einem oder mehreren base-specifier s.

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) Gibt eine nicht-virtuelle Vererbung mit standardmäßiger Member-Zugreifbarkeit an.
2) Gibt eine virtuelle Vererbung mit standardmäßiger Member-Zugreifbarkeit an.
3) Gibt eine nicht-virtuelle Vererbung mit gegebener Member-Zugriffsbeschränkung an.
4) Gibt eine virtuelle Vererbung mit gegebener Member-Zugriffsebene an.
5) Gleich wie 4), virtual und access-specifier können in beliebiger Reihenfolge erscheinen.
attr - (seit C++11) Folge beliebig vieler Attribute
access-specifier - einer von private , public , oder protected
class-or-computed - einer von
  • nested-name-specifier  (optional) type-name
  • nested-name-specifier template simple-template-id
(seit C++11)
(seit C++26)

Ein elaborated type specifier kann aufgrund von Syntaxeinschränkungen nicht direkt als class-or-computed erscheinen.

base-specifier in einer base-clause können Pack-Erweiterungen sein.

Eine als final deklarierte Klasse oder Struktur kann nicht durch class-or-computed bezeichnet werden.

(seit C++11)

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

struct Base
{
    int a, b, c;
};
// jedes Objekt vom Typ Derived beinhaltet Base als Subobjekt
struct Derived : Base
{
    int b;
};
// jedes Objekt vom Typ Derived2 beinhaltet Derived und Base als Subobjekte
struct Derived2 : Derived
{
    int c;
};

Klassen, die durch class-or-computed 's in der base-clause aufgeführt sind, sind direkte Basisklassen. Deren Basisklassen sind indirekte Basisklassen. Dieselbe Klasse kann nicht mehr als einmal als direkte Basisklasse spezifiziert werden, aber dieselbe Klasse kann sowohl direkte als auch indirekte Basisklasse sein.

Jede direkte und indirekte Basisklasse ist als Basisklassen-Subobjekt innerhalb der Objektrepräsentation der abgeleiteten Klasse an einem ABI-abhängigen Offset vorhanden. Leere Basisklassen vergrößern aufgrund der Empty Base Optimization normalerweise nicht die Größe des abgeleiteten Objekts. Die Konstruktoren der Basisklassen-Subobjekte werden durch den Konstruktor der abgeleiteten Klasse aufgerufen: Argumente können diesen Konstruktoren in der Member Initializer List übergeben werden.

Virtuelle Basisklassen

Für jede unterschiedliche Basisklasse, die als virtual spezifiziert ist, enthält das most derived object nur ein Basisklassen-Subobjekt dieses Typs, selbst wenn die Klasse mehrfach in der Vererbungshierarchie auftritt (sofern sie jedes Mal virtual vererbt wird).

struct B { int n; };
class X : public virtual B {};
class Y : virtual public B {};
class Z : public B {};
// jedes Objekt vom Typ AA hat ein X, ein Y, ein Z und zwei B's:
// eines, das die Basis von Z ist, und eines, das von X und Y gemeinsam genutzt wird
struct AA : X, Y, Z
{
    AA()
    {
        X::n = 1; // modifiziert das Mitglied des virtuellen B-Subobjekts
        Y::n = 2; // modifiziert dasselbe Mitglied des virtuellen B-Subobjekts
        Z::n = 3; // modifiziert das Mitglied des nicht-virtuellen B-Subobjekts
        std::cout << X::n << Y::n << Z::n << '\n'; // gibt 223 aus
    }
};

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

Alle virtuellen Basis-Subobjekte werden vor jedem nicht-virtuellen Basis-Subobjekt initialisiert, daher ruft nur die most derived class die Konstruktoren der virtuellen Basen in ihrer member initializer list auf:

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() {} };
// Der Standardkonstruktor von AA ruft die Standardkonstruktoren von X und Y auf
// aber diese Konstruktoren rufen den Konstruktor von B nicht auf, da B eine virtuelle Basis ist
AA a; // a.n == 3
// Der Standardkonstruktor von X ruft den Konstruktor von B auf
X x;  // x.n == 1

Es gelten spezielle Regeln für unqualified name lookup bei Klassenmitgliedern, wenn virtuelle Vererbung involviert ist (manchmal auch als Dominanzregeln bezeichnet).

Öffentliche Vererbung

Wenn eine Klasse den public Member-Zugriffsbezeichner verwendet, um von einer Basisklasse abzuleiten, sind alle öffentlichen Member der Basisklasse als öffentliche Member der abgeleiteten Klasse zugreifbar und alle geschützten Member der Basisklasse sind als geschützte Member der abgeleiteten Klasse zugreifbar (private Member der Basis sind niemals zugreifbar, es sei denn, sie sind als Freund deklariert).

Öffentliche Vererbung modelliert die Subtyp-Beziehung der objektorientierten Programmierung: Das abgeleitete Klassenobjekt IST-EIN Basisklassenobjekt. Es wird erwartet, dass Referenzen und Zeiger auf ein abgeleitetes Objekt von jedem Code verwendet werden können, der Referenzen oder Zeiger auf eine ihrer öffentlichen Basisklassen erwartet (siehe LSP ) oder, in DbC -Begriffen, eine abgeleitete Klasse sollte die Klasseninvarianten ihrer öffentlichen Basisklassen beibehalten, sollte keine Vorbedingung verstärken oder keine Nachbedingung einer Memberfunktion abschwächen, die sie überschreibt .

#include <iostream>
#include <string>
#include <vector>
struct MenuOption { std::string title; };
// Menu ist ein Vektor von MenuOption: Optionen können eingefügt, entfernt, umgeordnet werden...
// und hat einen Titel.
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';
    }
};
// Hinweis: Menu::title ist nicht problematisch, da seine Rolle unabhängig von der Basisklasse ist.
enum class Color { WHITE, RED, BLUE, GREEN };
void apply_terminal_color(Color) { /* OS-spezifisch */ }
// DAS IST SCHLECHT!
// ColorMenu ist ein Menu, bei dem jede Option eine benutzerdefinierte Farbe hat.
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 benötigt die folgenden Invarianten, die nicht erfüllt werden können
// durch öffentliches Vererben von Menu, zum Beispiel:
// - ColorMenu::colors und Menu müssen die gleiche Anzahl an Elementen haben
// - Um sinnvoll zu sein, sollte erase() auch Elemente aus colors entfernen,
//   damit Optionen ihre Farben behalten
// Grundsätzlich wird jeder nicht-konstante Aufruf einer std::vector-Methode die Invariante
// des ColorMenu brechen und erfordert eine Korrektur durch den Benutzer durch korrektes Verwalten der Farben.
int main()
{
    ColorMenu color_menu;
    // Das große Problem dieser Klasse ist, dass wir ColorMenu::colors
    // mit Menu synchron halten müssen.
    color_menu.push_back(MenuOption{"Some choice"});
    // color_menu.print(); // FEHLER! colors[i] in print() ist außerhalb des Bereichs
    color_menu.colors.push_back(Color::RED);
    color_menu.print(); // OK: colors und Menu haben die gleiche Anzahl an Elementen
}

Geschützte Vererbung

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

Geschützte Vererbung kann für "kontrollierte Polymorphie" verwendet werden: innerhalb der Member von Derived sowie innerhalb der Member aller weiter abgeleiteten Klassen, ist die abgeleitete Klasse EIN Base: Referenzen und Zeiger auf Derived können dort verwendet werden, wo Referenzen und Zeiger auf Base erwartet werden.

Private-Vererbung

Wenn eine Klasse den private member access specifier verwendet, um von einer Basisklasse abzuleiten, sind alle öffentlichen und geschützten Member der Basisklasse als private Member der abgeleiteten Klasse zugreifbar (private Member der Basisklasse sind niemals zugreifbar, es sei denn, sie sind als friend deklariert).

Private Vererbung wird häufig im policy-basierten Design verwendet, da Policies normalerweise leere Klassen sind und ihre Verwendung als Basisklassen sowohl statische Polymorphie ermöglicht als auch die Empty-Base-Optimierung 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 bessere Kapselung und wird generell bevorzugt, es sei denn, die abgeleitete Klasse benötigt Zugriff auf protected-Member (einschließlich Konstruktoren) der Basis, muss ein virtuelles Member der Basis überschreiben, benötigt, dass die Basis vor und nach einem anderen Basis-Subobjekt konstruiert bzw. destruiert wird, eine virtuelle Basis teilen muss oder die Konstruktion einer virtuellen Basis kontrollieren muss. Die Verwendung von Members zur Implementierung von Komposition ist auch nicht anwendbar im Fall von Mehrfachvererbung von einem parameter pack oder wenn die Identitäten der Basisklassen zur Compilezeit durch Template-Metaprogrammierung bestimmt werden.

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

template<typename Transport>
class service : private Transport // private Vererbung von der Transport-Policy
{
public:
    void transmit()
    {
        this->send(...); // Senden mit dem bereitgestellten Transport
    }
};
// TCP-Transport-Policy
class tcp
{
public:
    void send(...);
};
// UDP-Transport-Policy
class udp
{
public:
    void send(...);
};
service<tcp> service(host, port); 
service.transmit(...); // Senden über TCP

Suche nach Elementnamen

Die Regeln für unqualifizierte und qualifizierte Namenssuche für Klassenmitglieder sind detailliert beschrieben in Namenssuche .

Schlüsselwörter

virtual

Fehlerberichte

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

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 1710 C++98 die Syntax von class-or-decltype machte es unmöglich, von
einer abhängigen Klasse abzuleiten, wo das template Disambiguierungszeichen erforderlich ist
erlaubt template

Siehe auch