Cinq, style de programmation orienté objet

  • La relation entre les classes dépend du modèle de programmation orienté objet (modèle de programmation orienté objet) à définir.
  • La programmation orientée objet est le trait le plus important est hérité (héritage) et polymorphisme (polymorphisme). Le premier organise un groupe de classes liées pour partager des données et des comportements d'exploitation communs, tandis que le second nous permet de manipuler un seul individu plutôt que des classes indépendantes, et nous donne la flexibilité d'ajouter et de supprimer n'importe quelle classe spécifique.
  • Le mécanisme d'héritage définit la relation parent-enfant. La classe parent définit toutes les sous-classes, l'interface publique commune ( interface publique) et l'implémentation privée (implémentation privée). Chaque sous-classe peut ajouter ou remplacer ce dont elle hérite pour obtenir son propre comportement unique.
  • En C ++, la classe parente est appelée classe de base (classe de base), est appelée classe dérivée de sous -classe (classe dérivée). La relation entre les classes parent et enfant est appelée hiérarchie d' héritage (hiérarchie d'héritage). Le système dit d'héritage se déduit de la partie la plus abstraite à la partie la plus concrète.
  • La classe de base abstraite ( classe de base abstraite) fait référence aux classes de base qui ne représentent pas une catégorie réelle, mais n'existent que pour les besoins de conception et sont utilisées pour définir des comportements d'opération courants.
  • Le polymorphisme permet au pointeur ou à la référence de la classe de base de pointer de manière transparente vers n'importe quel objet de classe dérivée. À moins que le programme ne soit réellement exécuté pour le moment, il est impossible de déterminer quel objet dérivé est pointé, et l'exécution de la fonction peut être différente à chaque fois.
  • Liaison dynamique ( liaison dynamique): l' opération d' analyse "est en fait appelée pour savoir quelle est une fonction d'une classe dérivée" sera retardée jusqu'à ce que l' exécution soit effectuée.
  • Liaison statique : avant que le programme ne soit exécuté, il a été résolu quelle fonction doit être appelée.
  • La fonction d'héritage nous permet de définir un ensemble de classes associées et de partager des interfaces communes. Le polymorphisme nous permet d'utiliser un type quel que soit le type (indépendant du type) opèrent de la même manière que les objets de classe.
  • Polymorphisme et propriétés de liaison dynamique uniquement lorsque la fonction de pointeur est utilisée ou référence.
  • Par défaut, l'analyse de la fonction membre est effectuée de manière statique au moment de la compilation . Pour le rendre dynamique à l'exécution , nous devons ajouter un mot-clé avant sa déclaration pour virtualindiquer qu'il s'agit d'une fonction virtuelle .
  • Lorsque le programme définit un objet dérivé, le constructeur de la classe de base et de la classe dérivée seront exécutés; lorsque l'objet dérivé est détruit, le destructeur de la classe de base et de la classe dérivée seront également exécutés (mais l'ordre est inversé, le premier est dérivé et ensuite la base).
  • Lors de la définition d'une classe dérivée, afin d'indiquer clairement que la nouvelle classe hérite d'une classe existante , son nom doit être :suivi de deux points , suivi du mot-clé publicet du nom de la classe de base . (* La classe de base peut être héritée de trois manières: publique, protégée ou privée. Ce livre ne traite que du public).
  • Avant qu'une classe ne fasse une déclaration d'héritage, la définition de sa classe de base doit déjà exister (le fichier d'en-tête contenant la définition de classe de base doit être inclus en premier).
  • protectedTous les membres déclarés comme étant directement accessibles par les classes dérivées, sauf qu'ils ne peuvent pas accéder directement aux membres protégés.
  • public, protected, privateTrois méthodes d'héritage, qui changent en conséquence les attributs d'accès des membres de la classe de base:
    • Héritage public : les attributs d'accès des membres publics de la classe de base, des membres protégés et des membres privés deviennent: public, protected, private dans la classe dérivée;
    • Héritage protégé : les attributs d'accès des membres publics de la classe de base, des membres protégés et des membres privés de la classe dérivée deviennent: protected, protected, private;
    • Héritage privé : les attributs d'accès des membres publics de la classe de base, des membres protégés et des membres privés deviennent privés, privés et privés dans la classe dérivée.
  • Mais quel que soit le type de méthode d'héritage, les deux points suivants n'ont pas changé:
    • Les membres privés ne sont accessibles que par les membres de cette classe (à l'intérieur de la classe) et les amis, et ne sont pas accessibles par les classes dérivées;
    • Les membres protégés sont accessibles par des classes dérivées.
  • Le modificateur d'accès par défaut pour les membres et les classes est private.
  • Lors de l'utilisation d'une classe dérivée, il n'est pas nécessaire de faire une distinction délibérée entre les membres hérités et les membres auto-définis. L'utilisation des deux est totalement transparente.
  • Les étapes pour définir une classe de base abstraite:
    • La première étape: découvrir les comportements d'opération communs de toutes les sous-classes Ces comportements représentent l'interface publique de la classe de base.
    • Deuxième étape: essayez de savoir quel comportement d'exploitation et le type de connexe (dépendant du type), c'est-à-dire le comportement dont les opérations doivent avoir des implémentations différentes en fonction de la classe dérivée. Ces comportements devraient devenir des fonctions virtuelles dans tout le système d'héritage de classes . * Une fonction membre statique ne peut pas être déclarée en tant que fonction virtuelle.
    • Étape 3: Essayez de trouver le niveau d'accès de chaque opération . Si le programme général est accessible public; s'il n'a pas besoin d'être utilisé en dehors de la classe de base, alors il l'est private(même la classe dérivée de la classe de base ne peut pas accéder au membre privé de la classe de base); la classe dérivée est accessible mais non autorisé L'accès au programme général est protected.
  • L'attribution d'une valeur de fonction virtuelle en 0fait une fonction virtuelle pure - pour cette classe, cette fonction virtuelle n'a pas de signification réelle. La fonction virtuelle pure n'a pas de définition de fonction et l'interface est incomplète.
  • Si une classe déclare une (ou plusieurs) fonctions virtuelles pures, le programme ne peut pas générer d'objets pour elle. Ce type de classe ne peut être utilisé que comme sous-objet de sa classe dérivée , et le principe est que ces classes dérivées doivent fournir des définitions exactes pour toutes les fonctions virtuelles.
  • Selon les règles générales, lorsqu'une classe de base définit une (ou plusieurs) fonctions virtuelles, son destructeur doit être déclaré comme virtuel:
//定义基类指针ps指向派生类Fibonacci的对象 
num_sequence *ps = new Fibonacci(12);
//...使用数列
delete ps; 


/*
当delete表达式应用于ps时,destructor会先应用于ps所指
的对象身上,于是此对象占用的内存空间归还。 

non-virtual函数在编译时便已完成解析,所以ps调用的
destructor一定是Fibonacci的,而不是基类的。 

正确做法是根据实际对象的类型选择调用哪一个destructor, 
因此解析操作应该在运行时进行。 
*/ 
  • La classe dérivée est composée de deux parties: le sous-objet de la classe de base (composé du membre de données non statique de la classe de base) + la partie de la classe dérivée (composée du membre de données non statique du membre dérivé classer).
  • La fonction virtuelle de la classe dérivée doit correspondre exactement au prototype de fonction dans la classe de base. Lors de la définition d'une fonction virtuelle en dehors de la classe, il n'est pas nécessaire de spécifier le mot-clé virtual.
  • En spécifiant l'objet appelant via l'opérateur d'étendue de classe, le mécanisme de fonction virtuelle peut être ignoré , de sorte que la fonction puisse être complètement résolue au moment de la compilation, au lieu d'attendre le moment de l'exécution.
  • Chaque classe dérivée a un membre avec le même nom que le membre de la classe de base, qui masquera le membre de la classe de base. Si vous souhaitez utiliser le membre hérité dans une classe dérivée , vous devez utiliser l'opérateur d'étendue de classe pour qualifier .
  • Si la fonction du même nom dans la classe de base et la classe dérivée n'est pas virtuelle , alors chaque fois que la fonction est appelée via le pointeur ou la référence de la classe de base, la copie analysée est la copie de la classe de base.
  • Le membre de données choisit le pointeur ou la référence:
    • La référence doit être initialisée dans la liste d'initialisation des membres du constructeur. Une fois initialisé, il ne peut plus pointer vers un autre objet.
    • Le pointeur peut être initialisé dans le constructeur, ou il peut être initialisé à null puis le faire pointer vers une adresse mémoire valide.
  • Le constructeur de la classe dérivée doit non seulement initialiser le membre de données de la classe dérivée, mais également fournir des valeurs appropriées pour le membre de données de sa classe de base .
  • Contrairement au constructeur, le destructeur de la classe de base sera automatiquement appelé après la fin du destructeur de la classe dérivée. Nous n'avons pas besoin de l'appeler explicitement dans la classe dérivée.
  • Si nous héritons d' une fonction virtuelle pure, alors cette classe dérivée sera également considérée comme une classe abstraite et aucun objet ne pourra être défini pour elle.
  • Si la couverture fournie par la fonction virtuelle de classe de base, la nouvelle classe dérivée définit son prototype de fonction doit fournir une déclaration de classe de base du prototype de fonction de conformité complète : liste de paramètres, type de retour, const (const-ness).
    • Exception: lorsque la fonction virtuelle de la classe de base renvoie une forme de la classe de base (généralement un pointeur ou une référence), la fonction du même nom dans la classe dérivée peut renvoyer le type dérivé de la classe de base:
      class num_sequence {
              
              
      public:
      	//派生类的clone()函数可以返回一个指针
      	//指向num_sequence的任何一个派生类 
      	virtual num_sequence *clone() = 0;
      	
      	//...
      }; 
      
      class Fibonacci : public num_sequence  {
              
              
      public:
      	//Fibonacci派生自num_sequence
      	//在派生类中,关键字virtual并非必要
      	Fibonacci *clone() {
              
               return new Fibonacci( *this ); }
      	
      	//... 
      };
      
  • Lorsqu'une classe dérivée remplace une fonction virtuelle et effectue une opération de déclaration, il n'est pas nécessaire d' ajouter des mots clés virtual. Le compilateur déterminera si une fonction remplacera la fonction du même nom de sa classe de base en fonction de la déclaration prototype des deux fonctions. .
  • Exceptions au mécanisme de fonction virtuelle: (1) dans le constructeur et le destructeur de la classe de base, (2) lorsque l'objet de la classe de base est utilisé à la place du pointeur ou de la référence de l'objet de la classe de base.

Lorsque nous construisons un objet de classe dérivé, le constructeur de la classe de base est appelé en premier. Si une fonction virtuelle est appelée dans le constructeur de la classe de base, celle définie par la classe dérivée sera-t-elle appelée?
Réponse: Non. Pour le moment, le membre de données de la classe dérivée n'a pas été initialisé et l'appel peut accéder au membre de données non initialisé à ce moment. Par conséquent, dans le constructeur de la classe de base, la fonction virtuelle de la classe dérivée ne sera jamais appelée . Il en va de même pour l'appel de fonctions virtuelles dans le destructeur de la classe de base.

Afin de pouvoir afficher plusieurs types dans un seul objet, le polymorphisme nécessite un niveau d'indirection.
En C ++, seuls les pointeurs et les références avec des classes de base peuvent prendre en charge le concept de programmation orientée objet.

void print (LibMat object,
			const LibMat *pointer,
			const LibMat &reference)
{
    
    
	//以下必定调用LibMat::print()
	object.print();
	
	//以下一定会通过虚函数机制来进行解析,
	//我们无法预知哪一个派生类的print()被调用
	pointer->print();
	reference.print(); 
} 

int main()
{
    
    
	AudioBook iWish("Her Pride of 10",
					"Stanley Lippman",
					"Jeremy Irons");
	print( iWish, &iWish, iWish );
	//...
}

Comme indiqué ci - dessus, nous déclarons un objet réel ( print()le premier paramètre) pour la classe de base , et en même temps allouer suffisamment de mémoire espace pour loger l'objet réel . Si un objet de classe dérivée ( AudioBook) est passé ultérieurement , il n'y a pas assez de mémoire pour placer chaque membre de données dans la classe dérivée.
Seuls les sous-objets de la classe de baseiWish interne (c'est-à- dire les composants auxquels ils appartiennent ) sont copiés dans la mémoire réservée à l'objet paramètre, et les autres sous-objets sont découpés. Quant aux deux autres paramètres et , il a été initialisé l' adresse mémoire de l'objet est localisé. C'est le point qu'ils peuvent compléter le sujet de la raison.LibMat
pointerreferenceiWishAudioBook

  • typeidLes opérateurs, pris en charge par le langage de programmation, font partie du mécanisme d' identification de type à l'exécution (RTTI). Interrogons le pointeur de classe polymorphe ou la référence de classe pour obtenir le type réel de l'objet auquel il fait référence.
  • Fichier d'en-tête #include <typeinfo>. typeidL'opérateur renvoie un objet type_info , qui stocke diverses informations liées au type. Chaque classe polymorphe correspond à un objet type_info , et la name()fonction de cet objet renvoie un const char * pour représenter le nom de la classe.
    #include <typeinfo>
    
    inline const char* num_sequence:: what_am_i() const
    {
          
          
    	return typeid( *this ).name();
    }
    
  • La classe type_info prend également en charge deux opérations de comparaison pour l'égalité et l'inégalité.
  • static_cast<>En fait, c'est potentiellement dangereux, car le compilateur ne peut pas confirmer si l'opération de conversion que nous avons effectuée est complètement correcte. dynamic_castL'opérateur fournit une conversion conditionnelle et est un opérateur RTTI.
  • La classe de base concrète est opposée à la classe de base abstraite, qui peut représenter les objets qui existent réellement dans l'application.
  • La fonction membre est une fonction spéciale. Les deux ont des types de retour, des noms de fonction, des listes de paramètres et des définitions de fonction. Cependant, la fonction membre est attachée à une certaine classe, elle peut être virtuelle, const ou statique .......
  • Le constructeur lui-même est un type spécial de fonction membre.

Je suppose que tu aimes

Origine blog.csdn.net/pppppppyl/article/details/114155754
conseillé
Classement