Herencia de Clases en C
Herencia de Clases en C
Herencia de Clases en C
La herencia es la capacidad de una clase (derivada) de heredar y extender los miembros de otra clase
(base). En la herencia pública, las clases derivadas pueden acceder y utilizar los miembros públicos de la
clase base, pero la clase derivada no tiene acceso directo a los miembros privados. Lo mismo aplica a los
amigos de la clase derivada, que tienen acceso a los miembros privados derivados, pero no a la clase base:
public:
Persona() = default;
Persona(std::string nombre) : nombre(nombre){};
std::string getNombre() const { return this->nombre; }
void setNombre(std::string nombre) { this->nombre = nombre; }
private:
std::string nombre{"?"};
};
public:
Jugador(std::string juego) : juego(juego){};
private:
std::string juego{"?"};
};
int main()
{
Jugador jugador("Volleyball");
jugador.setNombre("Marcos");
std::cout << jugador << std::endl; // Marcos (Volleyball)
return 0;
}
No es posible acceder a los miembros privados de la clase base desde la clase derivada y para ello
necesitamos crear getters públicos.
El kit de la cuestión es que mediante la cláusula protected podemos definir miembros que sean accesibles
solo desde clases derivadas:
En la herencia pública, los miembros públicos y protegidos permanecen igual en la clase derivada.
En la herencia protegida, los miembros públicos y protegidos se hacen protegidos en la clase derivada.
En la herencia privada, los miembros públicos y protegidos de la clase base se vuelven privados en la
clase derivada.
class Base
{
public:
int x;
protected:
int y;
private:
int z;
};
// Herencia pública
class PublicaDerivada : public Base
{
// x es public
// y es protected
// z no es accesible desde PublicaDerivada
};
// Herencia protegida
class ProtegidaDerivada: protected Base
{
// x es protected
// y es protected
// z no es accesible desde ProtegidaDerivada
};
// Herencia privada
class PrivadaDerivada: private Base
{
// x is privada
// y is privada
// z no es accesible desde PrivadaDerivada
};
Debemos tener en cuenta lo anterior, especialmente al heredar de clases derivadas, pues los miembros
habrán cambiado su visibilidad.
Resurrección de miembros
Sin embargo existe un método para recuperar o cambiar la visibilidad de los miembros en las clases
heredadas conocido como resurrección de miembros en el contexto.
La resurrección solo funcionará en miembros públicos y protegidos, los miembros privados de la clase
base no se pueden resucitar:
class Base
{
public:
int x;
protected:
int y;
private:
int z;
};
#include <iostream>
class A
{
public:
A() { std::cout << "Constructor de A" << std::endl; }
};
class B : public A
{
public:
B() { std::cout << "Constructor de B" << std::endl; }
};
class C : public B
{
public:
C() { std::cout << "Constructor de C" << std::endl; }
};
class D : public C
{
public:
D() { std::cout << "Constructor de D" << std::endl; }
};
int main()
{
D d;
return 0;
}
Constructor de A
Constructor de B
Constructor de C
Constructor de D
En caso de que las clases derivadas extiendan los parámetros de inicialización en el constructor, la forma
de abordar el problema es mediante inicialización listada con los propios constructores y luego los
miembros de la clase derivada, tal como ilustra este ejemplo:
class A
{
public:
int a;
A(int a) : a(a)
{
std::cout << "Constructor de A: "
<< "a -> " << a << std::endl;
}
};
class B : public A
{
public:
int b;
B(int a, int b) : A(a), b(b)
{
std::cout << "Constructor de B: "
<< "a -> " << a
<< ", b -> " << b << std::endl;
}
};
class C : public B
{
public:
int c;
C(int a, int b, int c) : B(a, b), c(c)
{
std::cout << "Constructor de C: "
<< "a -> " << a
<< ", b -> " << b
<< ", c -> " << c << std::endl;
}
};
class D : public C
{
public:
int d;
D(int a, int b, int c, int d) : C(a, b, c), d(d)
{
std::cout << "Constructor de D: "
<< "a -> " << a
<< ", b -> " << b
<< ", c -> " << c
<< ", d -> " << d << std::endl;
}
};
int main()
{
D d(1, 2, 3, 4);
return 0;
}
Constructor de A: a -> 1
Constructor de B: a -> 1, b -> 2
Constructor de C: a -> 1, b -> 2, c -> 3
Constructor de D: a -> 1, b -> 2, c -> 3, d -> 4
Herencia de los símbolos
Comentar por último que si en una clase derivada se redefinen los miembros de una clase base, se tomará
el comportamiento de la clase derivada y se descartará el de la clase base:
class Base
{
public:
std::string texto{"Hola mundo"};
void print()
{
std::cout << texto << " (publico) desde base\n";
}
};
private:
std::string texto{"Adios mundo"};
public:
void print()
{
std::cout << texto << " (privado) desde derivada\n";
}
};
main()
{
Base b;
b.print();
Derivada d;
d.print();
return 0;
}
#include<iostream>
using namespace std;
class A
{
public:
void unMetodoX(){cout<<"Soy de la clase A"<<endl;}
};
int main()
{
D obj;
obj.unMetodoX();
return 0;
};
#include <iostream>
class Persona {
private:
std::string nombre;
int edad;
void metodoPrivado() {
std::cout << "Solo puedo ser llamado desde dentro de la clase";
}
public:
// Constructor sin argumentos
Persona() {
std::cout << "Se llama al constructor";
this->metodoPrivado();
}
// Constructor con nombre y edad
Persona(std::string nombre, int edad) {
this->edad = edad;
this->nombre = nombre;
}
void saludar() {
std::cout << "Hola, me llamo " << this->nombre << " y mi edad es "
<< this->edad << "\n";
}
// Definido virtual para que se pueda sobrescribir ;)
virtual void saludarAmigo(std::string nombre) {
std::cout << "Hola " << nombre << ", me llamo " << this->nombre
<< " y mi edad es " << this->edad << "\n";
}
};
class Estudiante : public Persona {
public:
// Constructor vacío
Estudiante(std::string nombre, int edad, std::string escuela)
: Persona(nombre, edad) {}
int main() {
Persona p1("Luis", 21);
p1.saludar();
Persona p2;
p2.setEdad(1);
p2.setNombre("John Galt");
p2.saludar();
Estudiante e1("Luis", 3, "Ninguna");
e1.saludar();
e1.saludarAmigo("Pedro");
e1.estudiar();
}
Funciones virtuales
Una función virtual es una función miembro que se espera volver a definir en clases
derivadas. Cuando se hace referencia a un objeto de clase derivada mediante un
puntero o una referencia a la clase base, se puede llamar a una función virtual para ese
objeto y ejecutar la versión de la clase derivada de la función.
Las funciones virtuales garantizan que se llame a la función correcta para un objeto, con
independencia de la expresión utilizada para llamarla.
Suponga que una clase base contiene una función declarada como virtual y una clase
derivada define la misma función. La función de la clase derivada se invoca para los
objetos de la clase derivada, aunque se llame mediante un puntero o una referencia a la
clase base. En el ejemplo siguiente se muestra una clase base que proporciona una
implementación de la función PrintBalance y dos clases derivadas
// deriv_VirtualFunctions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class Account {
public:
Account( double d ) { _balance = d; }
virtual ~Account() {}
virtual double GetBalance() { return _balance; }
virtual void PrintBalance() { cerr << "Error. Balance not available for base
type." << endl; }
private:
double _balance;
};
int main() {
// Create objects of type CheckingAccount and SavingsAccount.
CheckingAccount checking( 100.00 );
SavingsAccount savings( 1000.00 );
En el código anterior, las llamadas a PrintBalance son idénticas, excepto por el objeto al
que apunta pAccount. Dado que PrintBalance es virtual, se llama a la versión de la
función definida para cada objeto. La función PrintBalance de las clases
derivadas CheckingAccount y SavingsAccount “reemplaza” la función en la clase
base Account.
Las funciones de clases derivadas reemplazan funciones virtuales de clases base solo si
son del mismo tipo. Una función de una clase derivada no puede diferir de una función
virtual de una clase base solo en su tipo de valor devuelto; la lista de argumentos
también debe ser diferente.
Al llamar a una función mediante punteros o referencias, se aplican las siguientes reglas:
// deriv_VirtualFunctions2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class Base {
public:
virtual void NameOf(); // Virtual function.
void InvokingClass(); // Nonvirtual function.
};
void Base::InvokingClass() {
cout << "Invoked by Base\n";
}
void Derived::InvokingClass() {
cout << "Invoked by Derived\n";
}
int main() {
// Declare an object of type Derived.
Derived aDerived;