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

Cualquier tipo de clase (ya sea declarado con class-key class o struct ) puede declararse como derivada de una o más clases base que, a su vez, pueden derivar de sus propias clases base, formando una jerarquía de herencia.

Contenidos

Sintaxis

La lista de clases base se proporciona en la cláusula base de la sintaxis de declaración de clase . La cláusula base consiste en el carácter : seguido de una lista separada por comas de uno o más especificadores base .

attr  (opcional) class-or-computed (1)
attr  (opcional) virtual class-or-computed (2)
attr  (opcional) access-specifier class-or-computed (3)
attr  (opcional) virtual access-specifier class-or-computed (4)
attr  (opcional) access-specifier virtual class-or-computed (5)
1) Especifica una herencia no virtual con accesibilidad de miembros predeterminada.
2) Especifica una herencia virtual con accesibilidad de miembro predeterminada.
3) Especifica una herencia no virtual con la accesibilidad de miembro dada.
4) Especifica una herencia virtual con la accesibilidad de miembro dada.
5) Igual que 4), virtual y access-specifier pueden aparecer en cualquier orden.
attr - (desde C++11) secuencia de cualquier número de atributos
access-specifier - uno de private , public , o protected
class-or-computed - uno de
  • nested-name-specifier  (opcional) type-name
  • nested-name-specifier template simple-template-id
(desde C++11)
(desde C++26)

Un elaborated type specifier no puede aparecer directamente como class-or-computed debido a limitaciones de sintaxis.

base-specifier s en una base-clause pueden ser expansiones de paquete .

Una clase o struct declarada final no puede ser denotada por class-or-computed .

(desde C++11)

Si access-specifier se omite, por defecto será public para clases derivadas declaradas con class-key struct y será private para clases derivadas declaradas con class-key class .

struct Base
{
    int a, b, c;
};
// cada objeto de tipo Derived incluye Base como un subobjeto
struct Derived : Base
{
    int b;
};
// cada objeto de tipo Derived2 incluye Derived y Base como subobjetos
struct Derived2 : Derived
{
    int c;
};

Las clases denotadas por class-or-computed 's listadas en la base-clause son clases base directas. Sus bases son clases base indirectas. La misma clase no puede especificarse como clase base directa más de una vez, pero la misma clase puede ser tanto clase base directa como indirecta.

Cada clase base directa e indirecta está presente, como subobjeto de clase base , dentro de la representación del objeto de la clase derivada en un desplazamiento dependiente del ABI. Las clases base vacías normalmente no incrementan el tamaño del objeto derivado debido a la optimización de base vacía . Los constructores de los subobjetos de clase base son llamados por el constructor de la clase derivada: los argumentos pueden ser proporcionados a esos constructores en la lista de inicialización de miembros .

Clases base virtuales

Para cada clase base distinta que se especifique virtual , el objeto más derivado contiene solo un subobjeto de clase base de ese tipo, incluso si la clase aparece muchas veces en la jerarquía de herencia (siempre que se herede virtual cada vez).

struct B { int n; };
class X : public virtual B {};
class Y : virtual public B {};
class Z : public B {};
// cada objeto de tipo AA tiene un X, un Y, un Z y dos B:
// uno que es la base de Z y uno que es compartido por X e Y
struct AA : X, Y, Z
{
    AA()
    {
        X::n = 1; // modifica el miembro del subobjeto virtual B
        Y::n = 2; // modifica el mismo miembro del subobjeto virtual B
        Z::n = 3; // modifica el miembro del subobjeto no virtual B
        std::cout << X::n << Y::n << Z::n << '\n'; // imprime 223
    }
};

Un ejemplo de una jerarquía de herencia con clases base virtuales es la jerarquía de iostreams de la biblioteca estándar: std::istream y std::ostream se derivan de std::ios usando herencia virtual. std::iostream se deriva tanto de std::istream como de std::ostream , por lo que cada instancia de std::iostream contiene un subobjeto std::ostream , un subobjeto std::istream , y solo un subobjeto std::ios (y, en consecuencia, un std::ios_base ).

Todos los subobjetos de base virtual se inicializan antes que cualquier subobjeto de base no virtual, por lo que solo la clase más derivada llama a los constructores de las bases virtuales en su lista de inicialización de miembros :

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() {} };
// el constructor por defecto de AA llama a los constructores por defecto de X e Y
// pero esos constructores no llaman al constructor de B porque B es una base virtual
AA a; // a.n == 3
// el constructor por defecto de X llama al constructor de B
X x;  // x.n == 1

Existen reglas especiales para la búsqueda de nombres no calificados para miembros de clase cuando está involucrada la herencia virtual (a veces referidas como las reglas de dominancia).

Herencia pública

Cuando una clase utiliza el public especificador de acceso de miembro para derivar de una base, todos los miembros públicos de la clase base son accesibles como miembros públicos de la clase derivada y todos los miembros protegidos de la clase base son accesibles como miembros protegidos de la clase derivada (los miembros privados de la base nunca son accesibles a menos que se establezca amistad).

La herencia pública modela la relación de subtipado de la programación orientada a objetos: el objeto de la clase derivada ES-UN objeto de la clase base. Se espera que las referencias y punteros a un objeto derivado sean utilizables por cualquier código que espere referencias o punteros a cualquiera de sus bases públicas (ver LSP ) o, en términos de DbC , una clase derivada debe mantener los invariantes de clase de sus bases públicas, no debe fortalecer ninguna precondición ni debilitar ninguna postcondición de una función miembro que override .

#include <iostream>
#include <string>
#include <vector>
struct MenuOption { std::string title; };
// Menu es un vector de MenuOption: las opciones pueden insertarse, eliminarse, reordenarse...
// y tiene un título.
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';
    }
};
// Nota: Menu::title no es problemático porque su función es independiente de la clase base.
enum class Color { WHITE, RED, BLUE, GREEN };
void apply_terminal_color(Color) { /* específico del sistema operativo */ }
// ¡ESTO ES INCORRECTO!
// ColorMenu es un Menu donde cada opción tiene un color personalizado.
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 necesita los siguientes invariantes que no pueden satisfacerse
// heredando públicamente de Menu, por ejemplo:
// - ColorMenu::colors y Menu deben tener el mismo número de elementos
// - Para tener sentido, llamar a erase() debería eliminar también elementos de colors,
//   para permitir que las opciones mantengan sus colores
// Básicamente cada llamada no constante a un método de std::vector romperá el invariante
// del ColorMenu y necesitará corrección del usuario gestionando correctamente los colores.
int main()
{
    ColorMenu color_menu;
    // El gran problema de esta clase es que debemos mantener ColorMenu::Color
    // sincronizado con Menu.
    color_menu.push_back(MenuOption{"Some choice"});
    // color_menu.print(); // ¡ERROR! colors[i] en print() está fuera de rango
    color_menu.colors.push_back(Color::RED);
    color_menu.print(); // OK: colors y Menu tienen el mismo número de elementos
}

Herencia protegida

Cuando una clase utiliza protected especificador de acceso de miembro para derivar de una base, todos los miembros públicos y protegidos de la clase base son accesibles como miembros protegidos de la clase derivada (los miembros privados de la base nunca son accesibles a menos que se establezca amistad).

La herencia protegida puede utilizarse para "polimorfismo controlado": dentro de los miembros de Derived, así como dentro de los miembros de todas las clases derivadas posteriores, la clase derivada ES-UN base: las referencias y punteros a Derived pueden usarse donde se esperan referencias y punteros a Base.

Herencia privada

Cuando una clase utiliza el private especificador de acceso de miembro para derivar de una base, todos los miembros públicos y protegidos de la clase base son accesibles como miembros privados de la clase derivada (los miembros privados de la base nunca son accesibles a menos que se establezca amistad).

La herencia privada se utiliza comúnmente en el diseño basado en políticas, ya que las políticas suelen ser clases vacías, y utilizarlas como bases permite tanto el polimorfismo estático como el aprovechamiento de la optimización de base vacía .

La herencia privada también puede utilizarse para implementar la relación de composición (el subobjeto de la clase base es un detalle de implementación del objeto de la clase derivada). El uso de un miembro ofrece mejor encapsulación y generalmente es preferido a menos que la clase derivada requiera acceso a miembros protegidos (incluyendo constructores) de la base, necesite anular un miembro virtual de la base, necesite que la base se construya antes y se destruya después de algún otro subobjeto base, necesite compartir una base virtual o necesite controlar la construcción de una base virtual. El uso de miembros para implementar composición tampoco es aplicable en el caso de herencia múltiple desde un parameter pack o cuando las identidades de las clases base se determinan en tiempo de compilación mediante metaprogramación de plantillas.

Similar a la herencia protegida, la herencia privada también puede utilizarse para polimorfismo controlado: dentro de los miembros de la clase derivada (pero no dentro de clases más derivadas), la derivada ES-UN base.

template<typename Transport>
class service : private Transport // herencia privada de la política de Transporte
{
public:
    void transmit()
    {
        this->send(...); // enviar usando cualquier transporte proporcionado
    }
};
// política de transporte TCP
class tcp
{
public:
    void send(...);
};
// política de transporte UDP
class udp
{
public:
    void send(...);
};
service<tcp> service(host, port); 
service.transmit(...); // enviar a través de TCP

Búsqueda de nombre de miembro

Las reglas de búsqueda de nombres calificados y no calificados para miembros de clase se detallan en name lookup .

Palabras clave

virtual

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 1710 C++98 la sintaxis de class-or-decltype imposibilitaba derivar de
una clase dependiente donde se requiere el template desambiguador
se permite template

Véase también