herencia - Clase abstracta C++: constructor sí o no?
herencia en c++ pdf (8)
"Una clase abstracta contiene al menos una función virtual pura. Usted declara una función virtual pura mediante el uso de un especificador puro (= 0) en la declaración de una función de miembro virtual en la declaración de clase".
respecto a:
void Boss::Boss (const char* first, const char* last, double s)
: Employee (first, last)
first
y el last
se definen en la clase base, por lo tanto, para inicializarlos, necesitamos hacer una llamada al constructor de la clase base : Employee (first, last)
Una clase con una (o más) funciones virtuales puras es abstracta y no puede usarse para crear un nuevo objeto, por lo que no tiene un constructor.
Estoy leyendo un libro que proporciona el siguiente ejemplo:
class Employee {
public:
Employee(const char*, const char*);
~Employee();
const char* getFirstName() const;
const char* getLastName() const;
virtual double earnings() const=0 // pure virtual => abstract class
virtual void print() const
private:
char* firstName, lastName;
};
Si la clase es abstracta, ¿por qué tenemos un constructor? Utiliza esta clase más adelante (Boss es público derivado de Employee):
void Boss::Boss (const char* first, const char* last, double s)
: Employee (first, last)
El objetivo de la clase Abstract es que desee ampliar algunas funcionalidades por clases derivadas. ¿Puede tener constructor? Sí, puede y el propósito es inicializar variables locales de la clase base. Debe evitar el uso de constructor público en Resumen y usar protegido únicamente.
El ejemplo que tienes no es un buen ejemplo. No estoy seguro de qué libro es, pero el mal ejemplo. Es como definir la variable int con el nombre de la variable "iAmString" :).
int iAmString = 12;
Aclamaciones
Estás en lo cierto cuando dices que una clase que tiene una función virtual pura es abstracta y no se puede crear una instancia. Pero te equivocas cuando dices que no puede tener un constructor.
De hecho, como muestra su ejemplo, una clase abstracta puede tener miembros privados, que pueden ser utilizados por las funciones miembro de esta clase. Y estos miembros deben ser inicializados. Un constructor es una forma de hacerlo (por ejemplo, con una lista de inicialización en la clase derivada, como muestra la segunda muestra), mejor en mi opinión que una función init()
, por ejemplo.
Editar mi comentario en la respuesta : una clase abstracta puede tener variables miembro y funciones de miembros potencialmente no virtuales, de modo que cada clase derivada de la anterior implemente funciones específicas.
Entonces, la responsabilidad de la inicialización de estas variables miembro puede pertenecer a la clase abstracta ( al menos siempre para miembros privados , porque la clase derivada no podría inicializarlos, pero podría usar algunas funciones heredadas de miembros que pueden usar / confiar) en estos miembros). Por lo tanto, es perfectamente razonable que las clases abstractas implementen constructores.
La clase Employee
tiene datos y estos datos deben inicializarse de alguna manera. Constructor es una buena forma de hacerlo.
Para inicializar firstName y lastName. De lo contrario, deberá escribir un código para iniciarlos en los constructores de cada clase derivada.
Si la clase abstracta base no tiene un constructor, ¿cómo asignaría valores a los miembros firstname , lastname
para cualquier clase derivada, cuando está creando un objeto de la clase derivada?
Supongamos que hay una Manager Class
derivada de Employee
que agrega datos Salary
e implementa los earning()
. Ahora Employee
es una clase abstracta, pero Manager
es una concrete class
y, por lo tanto, puede tener un objeto de Manager
. Pero cuando está instalando Manager
, necesita inicializar / asignar valores a los miembros heredados de la base class ie Employee
. Una forma es que puede tener setFirstName() & setLastName()
en la clase base para este propósito y puede usarlos en el constructor para la derived class ie Manager
o una forma más conveniente sería tener un constructor en su base abstract class Employee
.
Vea el código a continuación:
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
class Employee {
public:
Employee(const char*, const char*);
~Employee();
const char* getFirstName() const;
const char* getLastName() const;
virtual double earnings() const=0; // pure virtual => abstract class
virtual void print() const;
private:
char* firstname;
char* lastname;
};
Employee::Employee(const char* first, const char* last){
firstname= (char*) malloc((strlen(first)+1)*sizeof(char));
lastname= (char*) malloc((strlen(last)+1)*sizeof(char));
strcpy(firstname,first);
strcpy(lastname,last);
}
Employee::~Employee(){
free(firstname);
free(lastname);
cout << "Employee destructed" << endl;
}
const char* Employee::getFirstName() const{ return firstname;}
const char* Employee::getLastName() const{ return lastname; }
void Employee::print() const{
cout << "Name: " << getFirstName() << " " << getLastName() << endl;
}
class Manager:public Employee{
public:
Manager(char* firstname,char* lastname,double salary):
Employee(firstname,lastname),salary(salary){}
~Manager(){}
double earnings() const {return salary;}
private:
double salary;
};
int main(){
Manager Object("Andrew","Thomas",23000);
Object.print();
cout << " has Salary : " << Object.earnings() << endl;
return 0;
}
Una clase con una función virtual pura no puede ser instanciada. Se espera que tenga subclases que lo extiendan y proporcionen la funcionalidad faltante.
Estas subclases construirán la clase base cuando se creen instancias, llamarán al constructor de su superclase y por eso las clases abstractas tienen constructores en c ++.
Por lo tanto, no puede crear una instancia directamente y llamar al constructor directamente, pero las subclases futuras lo harán.
firstName y lastName son miembros privados, no accesibles para Boss. Toda la interfaz a esos debe estar presente en la clase Employee, incluida la inicialización.