Palabra clave de C++: anular (anular)

 1. Función de anulación

El papel de la palabra clave override:

Si una clase derivada usa el descriptor de anulación al declarar una función virtual, la función debe anular la función con el mismo nombre en su clase base; de ​​lo contrario, el código no podrá compilarse.

C++ anular literalmente significa cobertura. De hecho, en C++, anula un método y lo reescribe para lograr diferentes funciones.

override es una palabra clave de control de herencia en C++11.

override asegura que la función sobrecargada declarada en la clase derivada tenga la misma declaración que la función virtual de la clase base.

override significa explícitamente que una función es una sobrecarga de una función virtual en la clase base . Más importante aún, comprueba las discrepancias de firmas entre las funciones virtuales de la clase base y las funciones sobrecargadas en las clases derivadas. El compilador emite un mensaje de error si las firmas no coinciden.

Override indica que la función debe reemplazar la función virtual en la clase base (usada en la función virtual de la clase derivada).

En nuestro proceso de programación en C++:

  • Lo más familiar es la implementación de los métodos de la interfaz.Generalmente , solo los métodos se declaran en la interfaz, y cuando implementamos, necesitamos implementar todos los métodos declarados por la interfaz.
  • Otra aplicación típica es que en la herencia, también es posible anular el método de la clase padre en la subclase .

La herencia pública consta de dos partes: una es "interfaz" y la otra es "implementación".

2. Aplicación de override en clase base y clase derivada

Por ejemplo, los métodos de herencia de varias funciones miembro de la clase Person:

class Person {
public:
    virtual void eat() const = 0;               // 1.纯虚函数
    virtual void say(const std::string &msg);   // 2.普通虚函数
    int name() const;                           // 3.非虚函数
};
 
class Student : public Person {
public:
protected:
private:
};
 
class Teacher : public Person {
public:
protected:
private:
};

1. Funciones virtuales puras

La función virtual pura hereda la interfaz de la función miembro de la clase base, y la implementación de la función debe reescribirse en la clase derivada :

Person* s1 = new Student;
s1->eat();           // calls Student::eat();
 
Person* t1 = new Teacher;
t1->eat();           // calls Teacher::eat();

Si desea llamar a Eat() de la clase base, debe agregar el operador de ámbito de clase ::

s1->Person::eat();    // calls Person::eat();

2. Funciones virtuales ordinarias

Las funciones virtuales ordinarias corresponden a una implementación predeterminada definida en la clase base, lo que significa que la interfaz y la implementación predeterminada de las funciones miembro de la clase base se heredan, y la clase derivada puede elegir si reescribir la función.

De hecho, es peligroso permitir que las funciones virtuales ordinarias hereden tanto la interfaz como la implementación predeterminada. De la siguiente manera, CarA y CarB son dos tipos de Car y funcionan exactamente de la misma manera.
 

class Car {
public:
    virtual void run(const Car &destination);
 
};
 
class CarA : public Car {
public:
protected:
private:
};
 
class CarB : public Car {
public:
protected:
private:
};

Este es un diseño típico orientado a objetos, dos clases comparten una característica, se  puedenrun  implementar en la clase base y heredar por las dos clases derivadas.run


Ahora agregue un nuevo modelo de avión CarC, cuyo modo de vuelo es diferente de CarA y CarB, si accidentalmente olvida volver a escribir la nueva función en CarC  fly .

class CarC : public Car
{
public:
	... // no fly function is declared
};

Llamar  run a la función en CarC es llamar  Car::run, pero el modo de operación de CarC es diferente al predeterminado

Car * car1 = new CarC;
car1->run(China);  // calls Car::run!!

Esto es lo que dije antes. Es peligroso que las funciones virtuales ordinarias hereden tanto las interfaces como las implementaciones predeterminadas. Es mejor implementar el comportamiento predeterminado en la clase base, pero solo proporcionar el comportamiento predeterminado cuando la clase derivada lo requiera.

  1. Un método es función virtual pura + implementación predeterminada. Debido a que es una función virtual pura, solo se hereda la interfaz y su implementación predeterminada no se heredará. Las clases derivadas que quieran usar esta implementación predeterminada deben llamar explícitamente:
    class Car
    {
    public:
    	virtual void run(const Car& destination) = 0;
     
    };
     
    void Car::run(const Car& destination)
    {
    	// a pure virtual function default code for run a Car to the given destination
    }
     
    class CarA : public Car
    {
    public:
    	virtual void run(const Car& destination)
    	{
    		Car::run(destination);
    	}
     
    };
    

    De esta forma, en la clase derivada CarC, incluso si accidentalmente olvida anular la función Ejecutar, no se llamará a la implementación predeterminada de Car.

     
    class CarC : public Car
    {
    public:
    	virtual void run(const Car& destination);
    };
     
    void CarC::run(const Car& destination)
    {
    	// code for run a CarC Car to the given destination
    }
    

  2. Método 2:
    como puede ver, la clave del problema anterior es que si accidentalmente  CarColvida anular  runla función en la clase derivada, la palabra clave anular se puede usar en C++ 11 para evitar tal "accidental".

3. Funciones no virtuales

La función de miembro no virtual no tiene la palabra clave virtual, lo que significa que la clase derivada no solo hereda la interfaz, sino que también hereda una implementación obligatoria.Dado que se hereda una implementación obligatoria, en la clase derivada, no hay necesidad de redefinir la clase heredada de la clase base Las funciones de los miembros son las siguientes:

Use el puntero para llamar a la función de nombre, luego llame a Person::name()
 

Student s1;  // s1 is an object of type Student
 
Person* p1 = &s1;  // get pointer to s1
p1->name();        //call name() through pointer
 
Student* s2 = &s1;   // get pointer to s1
s2->name();          // call name() through pointer

Si el nombre de la función miembro heredado de la clase base se redefine en la clase derivada:

class Student : public Person
{
public:
   int name() const; // hides Person::name();
 
};
 
p1->name();        //call name() through pointer
s2->name();          // call name() through pointer

En este momento, la función miembro redefinida en la clase derivada  "ocultará" (hide)  la función miembro heredada de la clase base.

Esto se debe a que las funciones no virtuales están "vinculadas estáticamente" y p1lo que se declara es  Person* un puntero de tipo, por lo que  p1las funciones no virtuales que se llaman están todas en la clase base, incluso si apuntan a clases derivadas.

Lo contrario de "enlace estático" es el "enlace dinámico" de funciones virtuales, es decir, ya sea que  p1se declare como  Person* o  Student* tipo, la función virtual que llama depende del p1tipo de objeto al que realmente apunta.

Tres, anular la reescritura

Agregar la palabra clave de anulación en el programa puede evitar el error de olvidar anular la función virtual en la clase derivada.

El siguiente es un ejemplo de los cuatro errores que son fáciles de cometer al reescribir funciones virtuales

class Base
{
public:
	virtual void fun1() const;
	virtual void fun2(int x);
	virtual void fun3() &;
	void fun4() const;    // is not declared virtual in Base
 
};
 
class Derived : public Base
{
public:
	virtual void fun1();  
	// declared const in Base, but not in Derived
	
	virtual void fun2(unsigned int x);  
	// takes an int in Base, but an unsigned int in Derived
	
	virtual void fun3() &&;   
	// is left-value-qualified in Base, but right-value-qualified in Derived
	
	void fun4() const;   
	
};

En la clase derivada, al anular la implementación de la función miembro heredada de la clase base, se deben cumplir las siguientes condiciones:

Uno virtual: en la clase base, la función miembro se declara como virtual (virtual)
Dos: en la clase base y la clase derivada, el tipo de retorno y la especificación de excepción de la función miembro deben ser compatibles
Cuatro similitudes: en la clase base y la clase derivada clase, nombres de funciones miembro, tipos de parámetros, atributos constantes (constness) y calificadores de referencia (calificador de referencia) deben ser idénticos

Tantas restricciones han llevado a la reescritura de funciones virtuales como el código anterior, que es muy fácil de cometer errores debido al descuido.

La palabra clave override en C++ 11 puede declarar explícitamente en la clase derivada qué funciones miembro deben anularse. Si no se anula, el compilador informará un error.

class Base
{
public:
	virtual void fun1() const;
	virtual void fun2(int x);
	virtual void fun3() &;
	void fun4() const;    // is not declared virtual in Base
 
};
 
class Derived : public Base
{
public:
	void fun1() const override; 
	void fun2(int x) override;  
	void fun3() & override;   
	void fun4() const;   
};
  1. herencia pública
    1.  Función virtual pura => heredada: interfaz
    2.  Función virtual ordinaria => heredada: interfaz + implementación predeterminada (implementación predeterminada)
    3.  Función miembro no virtual => heredada: interfaz + implementación obligatoria (implementación obligatoria) 
  2. Nunca redefina una función no virtual heredada
  3. Después de declarar la función que debe reescribirse, agregue la palabra clave override, de modo que incluso si accidentalmente se pierde una determinada condición severa para reescribir la función virtual, puede corregir rápidamente el error a través del informe de errores del compilador.

Preste atención a los siguientes puntos durante el uso:

  • La bandera del método cubierto debe coincidir exactamente con la bandera del método cubierto para lograr el efecto de la cobertura;
  • El valor de retorno del método invalidado debe ser coherente con el valor de retorno del método invalidado;
  • La excepción lanzada por el método cubierto debe ser coherente con la excepción lanzada por el método cubierto o su subclase;
  • El método anulado no puede ser privado, de lo contrario, un nuevo método simplemente se define en su subclase y no se sobrescribe.

Explicación detallada de C++-[override] Keywords_H Mujin's Blog-CSDN Blog_c++ override

Anulación de sobrescritura de sobrecarga de C/C++: se busca programador

Anulación de Java y Overload_w3cschool


---------------------
Autor: u013250861
Fuente: CSDN
Original: https://blog.csdn.net/u013250861/article/details/127873462
Declaración de derechos de autor: este artículo Artículo original para el autor, ¡adjunte el enlace de la publicación del blog para su reimpresión!
Análisis de contenido Por: CSDN, CNBLOG publicación de blog plug-in de reimpresión con un clic

Supongo que te gusta

Origin blog.csdn.net/xiaowang_lj/article/details/132024750
Recomendado
Clasificación