que - polimorfismo puro c++
¿Cuáles son los usos de las funciones virtuales puras en C++? (8)
Estoy aprendiendo acerca de C ++ en una clase en este momento y no me gustan las funciones virtuales puras. Entiendo que luego se describen en una clase derivada, pero ¿por qué querría declararlo igual a 0 si solo va a definirlo en la clase derivada?
Brevemente, es para hacer que la clase sea abstracta, de modo que no se pueda crear una instancia, pero una clase secundaria puede anular los métodos virtuales puros para formar una clase concreta. Esta es una buena manera de definir una interfaz en C ++.
Cualquier clase que contenga un método virtual puro será abstracta, es decir, no se puede crear una instancia. Las clases abstractas son útiles para definir algunos comportamientos centrales que las subclases deberían compartir, pero permiten (de hecho, requieren) subclases para implementar el resumen individualmente.
Un ejemplo de una clase abstracta:
class Foo {
// pure virtual, must be implemented by subclasses
virtual public void myMethod() = 0;
// normal method, will be available to all subclasses,
// but *can* be overridden
virtual public void myOtherMethod();
};
Una clase en la que cada método es abstracto puede usarse como una interfaz, requiriendo que todas las subclases se ajusten a la interfaz implementando todos los métodos contenidos en ella.
Un ejemplo de una interfaz:
class Bar {
// all method are pure virtual; subclasses must implement
// all of them
virtual public void myMethod() = 0;
virtual public void myOtherMethod() = 0;
};
Esencialmente, los virtuales puros se utilizan para crear una interfaz (similar a java). Esto se puede usar como un acuerdo entre dos módulos (o clases, o lo que sea) en cuanto a qué tipo de funcionalidad esperar, sin tener que saber nada sobre la implementación de la otra pieza. Esto le permite conectar y reproducir fácilmente las piezas utilizando la misma interfaz sin tener que cambiar nada en el otro módulo que usa su interfaz.
Por ejemplo:
class IStudent
{
public:
virtual ~IStudent(){};
virtual std::string getName() = 0;
};
class Student : public IStudent
{
public:
std::string name;
std::string getName() { return name; };
void setName(std::string in) { name = in; };
};
class School
{
public:
void sendStudentToDetention(IStudent *in) {
cout << "The student sent to detention is: ";
cout << in->getName() << endl;
};
};
int main()
{
Student student;
student.setName("Dave");
School school;
school.sendStudentToDetention(&student);
return 0;
}
La escuela no necesita saber cómo establecer el nombre de un estudiante, todo lo que necesita saber es cómo obtener el nombre del estudiante. Al proporcionar una interfaz para que los estudiantes la implementen y la escuela la use, existe un acuerdo entre las dos partes sobre qué funcionalidad necesita la escuela para realizar su trabajo. Ahora podemos activar y desactivar diferentes implementaciones de la clase de Estudiante todo lo que queramos sin afectar a la escuela (siempre que implementemos la misma interfaz cada vez).
Esto obliga a una clase derivada a definir la función.
Imagina que quiero modelar varios tipos de formas, y todas tienen un área bien definida. IShape
que cada forma debe heredar IShape
("I" para la interfaz), e IShape
incluirá un método GetArea()
:
class IShape {
virtual int GetArea();
};
Ahora el problema: ¿cómo debo calcular el área de una forma si esa forma no reemplaza a GetArea()
? Es decir, ¿cuál es la mejor implementación por defecto? Los círculos usan pi * radio ^ 2, los cuadrados usan longitud ^ 2, los paralelogramos y los rectángulos usan base * altura, los triángulos usan 1/2 base * altura, rombos, pentágonos, octágonos, etc. usan otras fórmulas.
Así que digo "si eres una forma, debes definir una forma de calcular el área, pero maldita sea si sé lo que será" definiendo el método virtual puro:
class IShape {
virtual int GetArea() = 0;
};
La idea con las clases abstractas es que todavía puede tener una variable declarada con ese tipo (es decir, es el tipo estático), pero la variable realmente se refiere o apunta a un tipo concreto real (el tipo dinámico).
Cuando invoca un método en C ++, el compilador necesita asegurarse de que el método sea compatible con ese objeto.
Al declarar la función virtual pura, está colocando un "marcador de posición" que el compilador puede usar para decir "oh ... Sé que todo lo que termine siendo referido por esta variable aceptará esa llamada" porque los tipos concretos reales se implementarán eso. Sin embargo, no es necesario proporcionar una implementación en el tipo abstracto.
Si no declarara nada, entonces el compilador no tendría una manera efectiva de garantizar que se implementaría en todos los subtipos.
Por supuesto, si está preguntando por qué querría hacer un resumen de clase, hay mucha información al respecto.
Los métodos virtuales puros en C ++ son básicamente una forma de definir interfaces sin requerir que se implementen.
Para agregar a la respuesta de Steven Sudit:
"En pocas palabras, es para hacer que la clase sea abstracta, de modo que no se pueda crear una instancia, pero una clase secundaria puede anular los métodos virtuales puros para formar una clase concreta. Esta es una buena manera de definir una interfaz en C ++".
Un ejemplo de esto sería si tuviera una clase base (tal vez Forma) que utilice para definir una cantidad de funciones miembro que puedan usar sus clases derivadas, pero desea evitar que se declare una instancia de Forma y obligue a los usuarios a usar solo la clases derivadas (que pueden ser, Rectángulo, Triángulo, Pentágono, etc.)
RE: la respuesta de Jeff arriba
Las clases no abstractas pueden contener funciones de miembro virtual y ser instanciadas. De hecho, para la sobrecarga de funciones miembro, esto es necesario ya que, de forma predeterminada, C ++ no determina el tipo de tiempo de ejecución de una variable, pero cuando se define utilizando la palabra clave virtual, lo hará.
Considere este código (nota, accesores, mutadores, constructores, etc. no se incluyen para mayor claridad):
class Person{
int age;
public:
virtual void print(){
cout << age <<endl;
}
}
class Student: public Person{
int studentID
public:
void print(){
cout << age << studentID <<endl;
}
}
Ahora al ejecutar este código:
Person p = new Student();
p.print();
sin la palabra clave virtual, solo se imprimiría la edad, no la edad y el ID de estudiante, como se supone que sucederá en la clase de Estudiantes
(este ejemplo se basa en uno muy similar de c ++ para programadores java http://www.amazon.com/Java-Programmers-Mark-Allen-Weiss/dp/013919424X )
@Steven Sudit: está completamente en lo cierto, descuidé incluir la herencia real, ¡doh! Los accesores, etc., no están incluidos para mantener las cosas más claras, y ahora lo he hecho más obvio. 3-7-09: todo arreglado