Mot-clé C++ : override (override)

 1. Fonction de dérogation

Le rôle du mot-clé override :

Si une classe dérivée utilise le descripteur de substitution lors de la déclaration d'une fonction virtuelle, la fonction doit remplacer la fonction portant le même nom dans sa classe de base, sinon la compilation du code échouera.

Remplacer C++ signifie littéralement couverture En fait, en C++, il remplace une méthode et la réécrit pour réaliser différentes fonctions.

override est un mot clé de contrôle d'héritage en C++11.

override garantit que la fonction surchargée déclarée dans la classe dérivée a la même déclaration que la fonction virtuelle de la classe de base.

override signifie explicitement qu'une fonction est une surcharge d'une fonction virtuelle dans la classe de base . Plus important encore, il vérifie les incompatibilités de signature entre les fonctions virtuelles de la classe de base et les fonctions surchargées dans les classes dérivées. Le compilateur émet un message d'erreur si les signatures ne correspondent pas.

Override indique que la fonction doit remplacer la fonction virtuelle dans la classe de base (utilisée dans la fonction virtuelle de la classe dérivée).

Dans notre processus de programmation C++ :

  • La chose la plus familière est l'implémentation des méthodes d'interface , généralement seules les méthodes sont déclarées dans l'interface, et lors de l'implémentation, il faut implémenter toutes les méthodes déclarées par l'interface.
  • Une autre application typique est qu'en héritage, il est également possible de redéfinir la méthode de la classe parente dans la sous-classe .

L'héritage public se compose de deux parties : l'une est « l'interface » et l'autre est « la mise en œuvre ».

2. Application du remplacement dans la classe de base et la classe dérivée

Par exemple, les méthodes d'héritage de plusieurs fonctions membres de la classe 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. Fonctions virtuelles pures

La fonction virtuelle pure hérite de l'interface de la fonction membre de la classe de base, et l'implémentation de la fonction doit être réécrite dans la classe dérivée :

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

Si vous souhaitez appeler Eat() de la classe de base, vous devez ajouter l'opérateur de portée de classe ::

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

2. Fonctions virtuelles ordinaires

Les fonctions virtuelles ordinaires correspondent à une implémentation par défaut définie dans la classe de base, ce qui signifie que l'interface et l'implémentation par défaut des fonctions membres de la classe de base sont héritées et que la classe dérivée peut choisir de réécrire la fonction.

En fait, il est dangereux de permettre à des fonctions virtuelles ordinaires d'hériter à la fois de l'interface et de l'implémentation par défaut. Comme suit, CarA et CarB sont deux types de Car, et ils fonctionnent exactement de la même manière.
 

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

Il s'agit d'une conception orientée objet typique, deux classes partagent une caractéristique -  runrunpeut être implémentée dans la classe de base et héritée par les deux classes dérivées.


Ajoutez maintenant un nouveau modèle d'avion CarC, dont le mode de vol est différent de CarA et CarB, si vous oubliez accidentellement de réécrire la nouvelle fonction dans CarC  fly .

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

L'appel  run de la fonction dans CarC appelle  Car::run, mais le mode de fonctionnement de CarC est différent de celui par défaut

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

C'est ce que j'ai dit auparavant. Il est dangereux pour les fonctions virtuelles ordinaires d'hériter à la fois des interfaces et des implémentations par défaut. Il est préférable d'implémenter le comportement par défaut dans la classe de base, mais de ne fournir le comportement par défaut que lorsque la classe dérivée l'exige.

  1. Une méthode est fonction virtuelle pure + implémentation par défaut. Comme il s'agit d'une fonction virtuelle pure, seule l'interface est héritée et son implémentation par défaut ne sera pas héritée. Les classes dérivées qui souhaitent utiliser cette implémentation par défaut doivent appeler explicitement :
    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 cette façon, dans la classe dérivée CarC, même si vous oubliez accidentellement de remplacer la fonction Run, l'implémentation par défaut de Car ne sera pas appelée.

     
    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éthode 2 :
    Comme vous pouvez le voir, la clé du problème ci-dessus est que si vous  CarCoubliez accidentellement de remplacer  runla fonction dans la classe dérivée, le mot-clé override peut être utilisé en C++11 pour éviter un tel "accidentel".

3. Fonctions non virtuelles

La fonction membre non virtuelle n'a pas le mot clé virtual, ce qui signifie que la classe dérivée hérite non seulement de l'interface, mais hérite également d'une implémentation obligatoire. Puisqu'une implémentation obligatoire est héritée, dans la classe dérivée, il n'est pas nécessaire de redéfinir la classe héritée de la classe de base. Fonctions membres, comme suit :

Utilisez le pointeur pour appeler la fonction name, puis appelez 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 le nom de la fonction membre hérité de la classe de base est redéfini dans la classe dérivée :

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

A ce moment, la fonction membre redéfinie dans la classe dérivée va  "masquer" (masquer)  la fonction membre héritée de la classe de base.

En effet, les fonctions non virtuelles sont "statiquement liées" et p1ce qui est déclaré est  Person* un pointeur de type, de sorte que  p1les fonctions non virtuelles appelées sont toutes dans la classe de base, même si elles pointent vers des classes dérivées.

L'opposé de la "liaison statique" est la "liaison dynamique" des fonctions virtuelles, c'est-à-dire, qu'elle soit  p1déclarée as  Person* ou  Student* type, la fonction virtuelle qu'elle appelle dépend du p1type d'objet réellement pointé.

Trois, Remplacer la réécriture

L'ajout du mot-clé override dans le programme peut éviter l'erreur d'oublier de remplacer la fonction virtuelle dans la classe dérivée.

Voici un exemple des quatre erreurs faciles à commettre lors de la réécriture de fonctions virtuelles

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;   
	
};

Dans la classe dérivée, lors du remplacement de l'implémentation de la fonction membre héritée de la classe de base, les conditions suivantes doivent être remplies :

Un virtuel : dans la classe de base, la fonction membre est déclarée comme virtuelle (virtuelle)
Deux : dans la classe de base et la classe dérivée, le type de retour et la spécification d'exception de la fonction membre doivent être compatibles
Quatre similitudes : dans la classe de base et la classe dérivée la classe, les noms des fonctions membres, les types de paramètres, les attributs constants (constness) et les qualificateurs de référence (qualificatif de référence) doivent être identiques

Tant de restrictions ont conduit à la réécriture de fonctions virtuelles telles que le code ci-dessus, qui est très facile à faire des erreurs en raison de la négligence.

Le mot-clé override en C++11 peut déclarer explicitement dans la classe dérivée quelles fonctions membres doivent être remplacées. Si ce n'est pas le cas, le compilateur signalera une erreur.

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. héritage public
    1.  Fonction virtuelle pure => héritée : interface
    2.  Fonction virtuelle ordinaire => héritée : interface + implémentation par défaut (implémentation par défaut)
    3.  Fonction membre non virtuelle => héritée : interface + implémentation obligatoire (implémentation obligatoire) 
  2. Ne jamais redéfinir une fonction non virtuelle héritée
  3. Après avoir déclaré la fonction qui doit être réécrite, ajoutez le mot clé override, de sorte que même si vous manquez accidentellement une certaine condition difficile pour réécrire la fonction virtuelle, vous pouvez rapidement corriger l'erreur via le rapport d'erreurs du compilateur.

Faites attention aux points suivants lors de l'utilisation :

  • L'indicateur de la méthode couverte doit correspondre exactement à l'indicateur de la méthode couverte pour obtenir l'effet de couverture ;
  • La valeur de retour de la méthode surchargée doit être cohérente avec la valeur de retour de la méthode surchargée ;
  • L'exception levée par la méthode couverte doit être cohérente avec l'exception levée par la méthode couverte, ou sa sous-classe ;
  • La méthode remplacée ne peut pas être privée, sinon une nouvelle méthode est juste définie dans sa sous-classe et elle n'est pas écrasée.

Explication détaillée de C++-[override] Keywords_H Mujin's Blog-CSDN Blog_c++ override

Remplacer l'écrasement de la surcharge C/C++ - Programmeur recherché

Remplacement et surcharge Java_w3cschool


---------------------
Auteur : u013250861
Source : CSDN Original
 : https://blog.csdn.net/u013250861/article/details/127873462
article Article original pour l'auteur, veuillez joindre le lien du blog pour réimpression !
Analyse de contenu Par : CSDN, blog CNBLOG, plug-in de réimpression en un clic

Je suppose que tu aimes

Origine blog.csdn.net/xiaowang_lj/article/details/132024750
conseillé
Classement