Herencia múltiple
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 53 de 104.
C++ permite que una clase herede de múltiples clases base simultáneamente. Esto se llama herencia múltiple y permite combinar características de diferentes jerarquías de clases.
Para heredar de múltiples clases, sepárelas con comas:
class Printer {
public:
void print() {
std::cout << "Printing document" << std::endl;
}
};
class Scanner {
public:
void scan() {
std::cout << "Scanning document" << std::endl;
}
};
class MultiFunctionDevice : public Printer, public Scanner {
public:
void copy() {
scan();
print();
}
};La clase MultiFunctionDevice ahora tiene acceso a los métodos de tanto Printer como Scanner:
MultiFunctionDevice mfd;
mfd.print(); // De Printer
mfd.scan(); // De Scanner
mfd.copy(); // Su propio métodoLos constructores se llaman en el orden en que las clases base aparecen en la declaración de herencia (de izquierda a derecha), y los destructores en orden inverso. Puedes pasar argumentos a cada constructor base en la lista de inicialización:
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};Surge un problema potencial cuando ambas clases base tienen un miembro con el mismo nombre. Esto crea ambigüedad, y debes usar el operador de resolución de ámbito para especificar a cuál te refieres:
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Llama a la versión de A
obj.B::show(); // Llama a la versión de BLa herencia múltiple es poderosa pero puede dar lugar a situaciones complejas, especialmente cuando las clases base comparten un ancestro común. Exploraremos ese desafío en la próxima lección.
Desafío
FácilVamos a construir un sistema de dispositivos para el hogar inteligente que combine capacidades de múltiples clases base utilizando herencia múltiple. Crearás dispositivos que puedan conectarse a WiFi y ser controlados por voz, y luego combinarás estas características en un altavoz inteligente unificado.
Organizarás tu código en tres archivos:
WiFiDevice.h: Define una claseWiFiDeviceque represente cualquier dispositivo capaz de conectarse a una red:- Un miembro protegido
std::string networkName - Un constructor que reciba un nombre de red y lo almacene
- Un método público
connect()que imprima:Connecting to WiFi: <networkName> - Un método público
status()que imprima:WiFi Status: Connected
- Un miembro protegido
VoiceControl.h: Define una claseVoiceControlque represente cualquier dispositivo con capacidades de voz:- Un miembro protegido
std::string assistantName - Un constructor que reciba un nombre de asistente y lo almacene
- Un método público
listen()que imprima:<assistantName> is listening... - Un método público
status()que imprima:Voice Status: Active
- Un miembro protegido
main.cpp: Lee tres entradas (cada una en una línea separada):- Nombre del dispositivo (string)
- Nombre de la red WiFi (string)
- Nombre del asistente de voz (string)
Define una clase
SmartSpeakerque herede públicamente tanto deWiFiDevicecomo deVoiceControl:- Un miembro privado
std::string deviceName - Un constructor que reciba los tres parámetros, pase el nombre de la red a
WiFiDevicey el nombre del asistente aVoiceControlutilizando la lista de inicialización, y almacene el nombre del dispositivo - Un método
powerOn()que imprima:<deviceName> powering on..., luego llame aconnect()ylisten() - Un método
showAllStatus()que imprima el nombre del dispositivo, luego use el operador de resolución de ámbito para llamar a ambos métodosstatus()para resolver la ambigüedad
Crea un objeto
SmartSpeakercon los valores de entrada. Llama apowerOn(), imprime una línea en blanco y luego llama ashowAllStatus().
Por ejemplo, con las entradas Echo, HomeNetwork, y Alexa:
Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...
Echo Status:
WiFi Status: Connected
Voice Status: ActiveEl método showAllStatus() debe imprimir el nombre del dispositivo seguido de " Status:" en una línea, luego llamar al método status() de cada clase base usando WiFiDevice::status() y VoiceControl::status() para evitar la ambigüedad, ya que ambas clases base tienen un método con el mismo nombre.
Hoja de referencia
C++ admite la herencia múltiple, lo que permite que una clase herede de múltiples clases base simultáneamente:
class DerivedClass : public BaseClass1, public BaseClass2 {
// Class body
};La clase derivada tiene acceso a los miembros de todas las clases base:
class Printer {
public:
void print() {
std::cout << "Printing document" << std::endl;
}
};
class Scanner {
public:
void scan() {
std::cout << "Scanning document" << std::endl;
}
};
class MultiFunctionDevice : public Printer, public Scanner {
public:
void copy() {
scan(); // From Scanner
print(); // From Printer
}
};Los constructores de las clases base se llaman en el orden en que aparecen en la lista de herencia (de izquierda a derecha). Utilice la lista de inicialización para pasar argumentos a cada constructor base:
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};Cuando varias clases base tienen miembros con el mismo nombre, utilice el operador de resolución de ámbito para resolver la ambigüedad:
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Call A's version
obj.B::show(); // Call B's versionPruébalo tú mismo
#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"
using namespace std;
// TODO: Definir la clase SmartSpeaker que herede públicamente tanto de WiFiDevice como de VoiceControl
// - Miembro privado: deviceName (string)
// - Constructor: toma deviceName, networkName, assistantName
// Usar la lista de inicialización para pasar networkName a WiFiDevice y assistantName a VoiceControl
// - Método powerOn(): imprime "<deviceName> powering on...", luego llama a connect() y listen()
// - Método showAllStatus(): imprime "<deviceName> Status:", luego llama a ambos métodos status()
// usando la resolución de ámbito (WiFiDevice::status() y VoiceControl::status())
class SmartSpeaker {
// TODO: Implementar la clase SmartSpeaker aquí
};
int main() {
string deviceName, networkName, assistantName;
getline(cin, deviceName);
getline(cin, networkName);
getline(cin, assistantName);
// TODO: Crear un objeto SmartSpeaker con los valores de entrada
// TODO: Llamar a powerOn()
// TODO: Imprimir una línea en blanco
// TODO: Llamar a showAllStatus()
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico