Partage de technologie

Bases du C (2)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Table des matières

1. Classes et objets

1.1 Définition de la classe

1.2 Qualificateurs d'accès

1.3 Domaine de classe

2. Instanciation

2.1 Notion d'instanciation

2.2 Taille de l'objet

3.ce pointeur

4.Fonctions membres par défaut des classes

4.1Constructeur

4.2 Destructeur

4.5 Surcharge de l'opérateur


1. Classes et objets

1.1 Définition de la classe

Format de définition de classe

class est le mot-clé qui définit la classe, Stack est le nom de la classe et {} est le corps de la classe. Notez que le point-virgule à la fin de la définition n'est pas omis. Le contenu du corps de la classe est appelé membres de la classe : les variables de la classe sont appelées attributs ou les variables membres de la classe, les fonctions de la classe sont appelées méthodes ou fonctions membres de la classe.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. //成员变量
  7. int* a;
  8. int top;
  9. int capacity;
  10. //成员函数
  11. void Push()
  12. {
  13. }
  14. void Pop()
  15. {
  16. }
  17. };//分号不能省略
  18. int main()
  19. {
  20. return 0;
  21. }

  • Afin de distinguer les variables membres, il est généralement d'usage d'ajouter un identifiant spécial à la variable membre, par exemple en commençant par _ ou m_ avant ou après la variable membre.Il n’y a aucune réglementation sur cette syntaxe C++, c’est juste une question de préférence personnelle ou d’entreprise.
  1. //为区分成员变量,一般前面加_
  2. //成员变量
  3. int* _a;
  4. int _top;
  5. int _capacity;
  • Struct en C++ peut également définir des classes. C++ est compatible avec l'utilisation de struct en C. En même temps, struct a été mis à niveau vers une classe. Le changement évident est que les fonctions peuvent également être définies dans struct. je recommande toujours d'utiliser class pour définir des classes.

  • Les membres définis dans une classe sont par défaut en ligne

1.2 Qualificateurs d'accès

C++ est un moyen d'implémenter l'encapsulation, en utilisant des classes pour combiner les propriétés et les méthodes d'un objet afin de rendre l'objet plus complet et de fournir de manière sélective son interface aux utilisateurs externes via des droits d'accès.

  • Les membres modifiés par public (public) sont directement accessibles en dehors de la classe, les membres modifiés par protected (protected) et private (private) ne sont pas directement accessibles en dehors de la classe, protected et private sont identiques
  • La portée de l'autorisation d'accès commence à partir de la position où l'autorisation d'accès apparaît jusqu'à ce que le qualificatif d'accès suivant apparaisse. S'il n'y a pas de qualificatif d'accès ultérieur, la portée se termine à }, qui correspond à la fin de la classe.
  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. ///
  7. void Push()
  8. {
  9. }
  10. //Push 没给限定符 class默认私有 private
  11. ///
  12. public:
  13. void Pop()
  14. {
  15. }
  16. int Swap()
  17. {
  18. }
  19. //Pop和Swap 被public修饰,直到下一个限定符出现之前都为公有
  20. ///
  21. protected:
  22. int add();
  23. //add 被public修饰,直到下一个限定符出现之前都为保护
  24. /// /
  25. private:
  26. int* _a;
  27. int _top;
  28. int _capacity;
  29. //成员变量被private修饰,直到}结束都为私有
  30. };
  31. int main()
  32. {
  33. Stack st;
  34. //公有可以访问
  35. st.Pop();
  36. st.Swap();
  37. //私有不可访问
  38. st._top;
  39. return 0;
  40. }

Notes complémentaires:

  1. Lorsque les membres de la définition de classe ne sont pas modifiés par les qualificateurs d'accès, ils sont par défaut privés et la structure est publique par défaut.
  2. Généralement, les variables membres seront limitées à privées/protégées, et les fonctions membres qui doivent être utilisées par d'autres seront placées comme publiques.

1.3 Domaine de classe

La classe définit une nouvelle portée.Tous les membres de la classe sont dans la portée de la classe. Lorsque vous définissez des membres en dehors de la classe, vous devez utiliser l'opérateur ::scope pour indiquer à quelle portée de classe appartient le membre.

Le domaine de classe affecte les règles de recherche de compilation. Si Init dans le programme suivant ne spécifie pas le domaine de classe Stack, le compilateur traitera Init comme une fonction globale. S'il ne trouve pas de membres tels que _top lors de la compilation, il ira dans le domaine de classe. domaine de classe pour les trouver.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. public:
  7. void Init(int x, int y);
  8. };
  9. void Stack::Init(int x, int y)
  10. {
  11. _top = x;
  12. _capacity = y;
  13. }
  14. int main()
  15. {
  16. return 0;
  17. }

Avis:

  1. Les déclarations de fonction et les définitions dans la classe sont séparées. Une fois la classe créée, un nouveau domaine de classe est formé. Le domaine de classe doit être spécifié, sinon il est inaccessible.

2. Instanciation

2.1 Notion d'instanciation

  • Le processus de création d’un type dans la mémoire physique est appelé instanciation de classe.
  • Une classe est une description abstraite d'un objet. C'est quelque chose comme un modèle. Elle limite les variables membres de la classe. Ces variables membres ne sont déclarées et aucun espace n'est alloué lorsqu'un objet est instancié avec une classe.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. //声明
  7. int* _a;
  8. int _top;
  9. int _capacity;
  10. };
  11. int main()
  12. {
  13. Stack::_top = 2024;
  14. //编译器报错,_top只是声明,并未实例化
  15. return 0;
  16. }
  • Une classe peut instancier plusieurs objets, et les objets instanciés occupent l'espace physique réel et stockent les variables membres. Par exemple : instancier des objets d'une classe, c'est comme utiliser des dessins de conception architecturale pour construire une maison dans la réalité. Les classes sont comme des dessins de conception qui prévoient le nombre de pièces, la taille de la maison, etc., mais il n'y a pas de données physiques. bâtiment, et il ne peut pas vivre dans les gens, ce n'est que lorsqu'une maison est construite à l'aide des dessins de conception que les gens peuvent vivre dans la maison.La même classe est comme un dessin de conception. Elle indique simplement au compilateur la quantité de mémoire qui va être ouverte, mais elle n'ouvre pas la mémoire. Seuls les objets instanciés se voient attribuer de la mémoire physique pour stocker les données.
    1. //text.cpp
    2. #include<iostream>
    3. using namespace std;
    4. class Stack
    5. {
    6. //声明
    7. int* _a;
    8. int _top;
    9. int _capacity;
    10. };
    11. int main()
    12. {
    13. Stack st;
    14. st._top=2024;
    15. //Stack实例化出st,系统已经给st分配内存了,可以存储数据,编译通过
    16. return 0;
    17. }

    2.2 Taille de l'objet

  • Analyser les membres de l'objet de classe ? Chaque objet instancié par une classe possède un espace de données indépendant, l'objet doit donc contenir des variables membres. Les fonctions membres sont-elles donc incluses ? Premièrement, une fois la fonction compilée, il s'agit d'une section d'instructions qui ne peut pas être stockée dans l'objet. Ces instructions sont stockées dans une zone séparée (segment de code). Si l'objet doit être stocké, il ne peut s'agir que d'un pointeur vers. une fonction membre. Est-il nécessaire de stocker des pointeurs dans l'objet ? Date instancie deux objets d1 et d2. Di et d2 ont tous deux des variables membres indépendantes _year/_month/_day pour stocker leurs propres données, mais les fonctions membres Init de d1 et d2. est le même, et il est gaspillé s'il est stocké dans l'objet. Si vous utilisez Date pour instancier 100 objets, le pointeur de fonction membre sera stocké à plusieurs reprises 100 fois, ce qui est trop inutile. En fait, le pointeur de fonction n'a pas besoin d'être stocké. Le pointeur de fonction est une adresse. La fonction appelante est compilée dans une instruction assembleur [adresse d'appel]. En fait, le compilateur doit trouver l'adresse de la fonction lors de la compilation et de la liaison. . Il n'est pas trouvé au moment de l'exécution. Il ne peut être trouvé que dynamiquement. L'état est trouvé au moment de l'exécution, l'adresse de la fonction doit donc être stockée.

Règles d'alignement de la mémoire

  • Le premier membre est à l'adresse décalée 0 par rapport à la structure
  • Les autres variables membres doivent être alignées à des adresses qui sont des multiples du numéro d'alignement.
  • Numéro d'alignement = Le plus petit entre le numéro d'alignement par défaut du compilateur et la taille du membre
  • Le numéro d'alignement par défaut de la plate-forme VS x64 est 4 et le numéro d'alignement par défaut de x86 est 8.
  • La taille totale de la structure est : un multiple entier du numéro d'alignement maximum (la plus grande de toutes les variables de type et le plus petit numéro d'alignement par défaut)
  • Si les structures sont imbriquées, la structure imbriquée est alignée sur un multiple entier de son propre numéro d'alignement maximum, et la taille globale de la structure est un multiple entier de tous les numéros d'alignement maximum (y compris le numéro d'alignement des structures imbriquées).
  1. class A
  2. {
  3. public:
  4. void Print()
  5. {
  6. cout << _ch << endl;
  7. }
  8. private:
  9. char _ch;
  10. int _i;
  11. };
  12. //_ch 是一个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存在_ch
  13. // _i是4个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存储_i
  14. class B
  15. {
  16. public:
  17. void Print()
  18. {
  19. //。。。
  20. }
  21. };
  22. class B
  23. {
  24. };
  25. //B和C里面没有存储任何成员变量,只有一个函数,可成员函数不存对象里面
  26. // 按理来说是0,但是结构体怎么会没大小,为表示对象存在C++对这种规定大小为1,为了占位标识对象存在

3.ce pointeur

Une fois le compilateur compilé, les fonctions membres de la classe ajouteront par défaut un pointeur vers la classe actuelle en première position du paramètre formel, appelé ce pointeur.

Par exemple, le prototype Init dans la classe Date est void Init (Date * const this, int year, intmonth, int day). Lors de l'accès aux variables membres dans les fonctions membres de la classe, l'essence est accessible via le pointeur this, comme _year dans la fonction Init, this-&gt;_year=year.

prototype:

  1. class Date
  2. {
  3. void Print()
  4. {
  5. cout << _year << "n" << _month << "n" << _day << endl;
  6. }
  7. void Init( int year, int month,int day)
  8. {
  9. _year = year;
  10. _month = month;
  11. _day = day;
  12. }
  13. private:
  14. int _year;
  15. int _month;
  16. int _day;
  17. };

  1. Date d1;
  2. d1.Init(2024,7,10);
  3. d1.Print();
  4. Date d2;
  5. d2.Init(2024, 7, 9);
  6. d2.Print();

vrai prototype

  1. class Date
  2. {
  3. void Init(Date* const this,int year, int month,int day)
  4. {
  5. this->_year = year;
  6. this->_month = month;
  7. this->_day = day;
  8. }
  9. void Printf(Date* const this)
  10. {
  11. cout << this->_year << "n" <<this-> _month << "n" << this->_day << endl;
  12. }
  13. private:
  14. int _year;
  15. int _month;
  16. int _day;
  17. };

  1. Date d1;
  2. d1.Init(&d1,2024,7,10);
  3. d1.Print(&d1);
  4. Date d2;
  5. d2.Init(&d2,2024, 7, 9);
  6. d2.Print();

C++ stipule qu'il n'est pas autorisé d'écrire ce pointeur à la place des paramètres réels et des paramètres formels (le compilateur le gérera lors de la compilation), mais ce pointeur peut être utilisé explicitement dans le corps de la fonction. Ce pointeur ne peut pas être modifié, mais le. le contenu pointé par ce pointeur peut

ce pointeur est stocké sur la pile

4.Fonctions membres par défaut des classes

La fonction membre par défaut est une fonction membre qui n'est pas explicitement définie par l'utilisateur et qui est automatiquement générée par le compilateur. Elle est appelée fonction membre par défaut.

4.1Constructeur

Le constructeur est une fonction membre spéciale. Il convient de noter que bien que le constructeur soit appelé constructeur, le contenu principal du constructeur n'est pas d'ouvrir de l'espace pour créer des objets (l'objet local que nous utilisons habituellement est l'espace qui est ouvert lorsque le cadre de pile est créé) ), mais l'objet est initialisé lorsque l'objet est instancié. L'essence du constructeur est de remplacer la fonction de la fonction Init que nous avons écrite auparavant dans les classes Stack et Date. L'appel automatique du constructeur remplace parfaitement la fonction Init.

Caractéristiques du constructeur :

  1. Le nom de la fonction est le même que le nom de la classe
  2. Aucune valeur de retour (vous n'avez rien à donner comme valeur de retour et n'écrivez pas void. C'est la règle du C++)
  3. Lorsque l'objet est instancié, le système appellera automatiquement le constructeur correspondant.
  4. Les constructeurs peuvent être surchargés
  5. S'il n'y a pas de constructeur explicite défini dans la classe, le compilateur C++ générera automatiquement un constructeur par défaut sans paramètre. Une fois que l'utilisateur aura explicitement défini le constructeur, le compilateur ne le générera plus.

  1. class Date
  2. {public:
  3. //1.无参构造函数
  4. Date()
  5. {
  6. _year = 1;
  7. _month = 1;
  8. _day = 1;
  9. }
  10. //2.带参构造函数
  11. Date(int year, int month, int day)
  12. {
  13. _year = year;
  14. _month = month;
  15. _day = day;
  16. }
  17. //3.全缺省构造函数
  18. Date(int year = 1, int month = 1, int day = 1)
  19. {
  20. _year = year;
  21. _month = month;
  22. _day = day;
  23. }
  24. private:
  25. int _year;
  26. int _month;
  27. int _day;
  28. };

Le constructeur sans paramètre, le constructeur par défaut et le constructeur généré par le compilateur par défaut lorsque nous n'écrivons pas de constructeur sont tous appelés constructeurs par défaut. Mais un seul de ces trois peut exister, pas en même temps. Bien que le constructeur sans paramètre et le constructeur par défaut complet constituent une surcharge de fonctions, il y aura une ambiguïté lors de leur appel.Notez que non seulement le constructeur par défaut est celui généré par le compilateur par défaut, c'est le constructeur, le constructeur sans paramètre, et le constructeur par défaut complet est également le constructeur par défaut. Pour résumer, il peut être appelé sans passer de paramètres.

Nous ne l'écrivons pas.La construction générée par le compilateur n'a aucune exigence pour l'initialisation des variables membres de type intégrées. Il est incertain qu'elle soit initialisée ou non, cela dépend du compilateur.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. typedef int STDataType;
  5. class Stack
  6. {
  7. public:
  8. Stack(int n = 4)
  9. {
  10. _a = (STDataType*)malloc(sizeof(STDataType) * n);
  11. if (nullptr == _a)
  12. {
  13. perror("malloc申请失败");
  14. }
  15. _capacity = n;
  16. _top = 0;
  17. }
  18. private:
  19. STDataType* _a;
  20. size_t _capacity;
  21. size_t _top;
  22. };
  23. //两个Stack实现队列
  24. class MyQueue
  25. {
  26. private:
  27. int size;
  28. Stack pushst;
  29. Stack popst;
  30. };
  31. int main()
  32. {
  33. MyQueue my;
  34. return 0;
  35. }

C++ divise les types en types personnalisés et types intégrés (types de base). Les types intégrés sont les types de données natifs fournis par le langage, tels que int/char/double/pointer, etc. Les types personnalisés sont des types que nous définissons nous-mêmes à l'aide de mots-clés tels que class/struct.Le constructeur ici est automatiquement initialisé et VS initialise également la taille de type intégrée. Différents compilateurs ont des valeurs d'initialisation différentes et C++ ne les spécifie pas.

Pour les variables membres de type personnalisé, il est nécessaire d'appeler le constructeur par défaut de cette variable membre pour l'initialiser.Si cette variable membre n'a pas de constructeur par défaut, une erreur sera signalée. Si nous voulons initialiser cette variable membre, nous devons utiliser une liste d'initialisation pour la résoudre.

Résumé : Dans la plupart des cas, nous devons implémenter le constructeur nous-mêmes. Dans quelques cas, il est similaire à MyQueue et lorsque Stack a un constructeur par défaut, MyQueue peut être automatiquement généré et utilisé.

4.2 Destructeur

  1. ~Stack()
  2. {
  3. free(_a);
  4. _a = nullptr;
  5. _top = _capacity = 0;
  6. }

Caractéristiques du destructeur :

1. Le nom du destructeur est précédé de caractères~

2. Aucun paramètre et aucune valeur de retour (conforme au constructeur)

3. Une classe ne peut avoir qu’un seul destructeur.Si la définition n'est pas affichée, le système générera automatiquement un destructeur par défaut.

4. Lorsque le cycle de déclaration d'objet se termine, le système appellera automatiquement le destructeur.

5. Semblable au constructeur, nous n'écrivons pas le destructeur généré automatiquement par le compilateur et ne traitons pas les membres de type intégrés qui appelleront d'autres destructeurs.

6. Il convient également de noter que lorsque nous affichons le destructeur, le destructeur du membre de type personnalisé sera également appelé, ce qui signifie que le destructeur du membre de type personnalisé sera automatiquement appelé quelle que soit la situation.

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. typedef int STDataType;
  5. class Stack
  6. {
  7. public:
  8. Stack(int n = 4)
  9. {
  10. _a = (STDataType*)malloc(sizeof(STDataType) * n);
  11. if (nullptr == _a)
  12. {
  13. perror("malloc申请失败");
  14. }
  15. _capacity = n;
  16. _top = 0;
  17. }
  18. ~Stack()
  19. {
  20. free(_a);
  21. _a = nullptr;
  22. _top=_capacity=0;
  23. }
  24. private:
  25. STDataType* _a;
  26. size_t _capacity;
  27. size_t _top;
  28. };
  29. //两个Stack实现队列
  30. class MyQueue
  31. {public:
  32. //编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化
  33. //编译器默认生成MyQueue的析构函数调用了Stack的析构,释放了Stack内部的资源
  34. //显示写析构也会调用Stack的析构
  35. ~MyQueue()
  36. {
  37. cout << "~MyQueue" << endl;
  38. }
  39. private:
  40. Stack pushst;
  41. Stack popst;
  42. };
  43. int main()
  44. {
  45. MyQueue my;
  46. return 0;
  47. }

Le destructeur de MyQueue ne fait rien, mais C++ stipule que d'autres destructeurs seront appelés pour libérer la mémoire.

Si aucune ressource n'est demandée, le destructeur n'a pas besoin d'être écrit et le destructeur par défaut généré par le compilateur peut être utilisé directement, comme Date. Si le destructeur par défaut généré peut être utilisé, il n'est pas nécessaire d'écrire explicitement le destructeur. comme MyQueue, mais il existe une application de ressources lors de la destruction, assurez-vous d'écrire directement le destructeur, sinon cela entraînera une fuite de ressources comme Stack.

4.5 Surcharge de l'opérateur

  • Lorsque des opérateurs sont utilisés sur des objets typés, le langage C++ nous permet de spécifier de nouvelles significations sous forme de surcharge d'opérateurs. C++ stipule que lorsqu'un objet de type classe utilise un opérateur, il doit être converti en appel à la surcharge d'opérateur correspondante. Sinon, le compilateur signalera une erreur.
  • La surcharge d'opérateur est une fonction avec un nom spécifique. Son nom est composé de l'opérateur et de l'opérateur à définir ultérieurement.Comme d'autres fonctions, elle a également son type de retour et sa liste de paramètres, ainsi qu'un corps de fonction
  1. bool operator<(Date d1, Date d2)
  2. {
  3. }
  4. bool operator==(Date d1,Date d2)
  5. {
  6. return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
  7. }
  • Une fonction opérateur surchargée prend autant de paramètres que de paramètres sur lesquels l'opérateur agit.Le confort de transport unaire a un paramètre et l'opérateur binaire a deux paramètres. L'opérande gauche de l'opérateur binaire est passé au premier paramètre et l'opérande droit est passé au deuxième paramètre.
  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Date
  5. {
  6. public:
  7. Date(int year, int month, int day)
  8. {
  9. _year= year;
  10. _month = month;
  11. _day = day;
  12. }
  13. int _year;
  14. int _month;
  15. int _day;
  16. };
  17. bool operator<(Date d1, Date d2)
  18. {
  19. }
  20. bool operator==(Date d1,Date d2)
  21. {
  22. return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
  23. }
  24. int main()
  25. {
  26. Date d1(2024, 7, 10);
  27. Date d2(2024,7,9);
  28. //两种用法都可以
  29. d1 == d2;
  30. operator==(d1 , d2);
  31. return 0;
  32. }
  • Si une fonction d'opérateur surchargée est une fonction membre, son premier opérande est transmis au pointeur implicite this par défaut. Par conséquent, lorsque l'opérateur est surchargé en tant que fonction membre, il a un paramètre de moins que l'opérande.
  • Une fois qu'un opérateur est surchargé, sa priorité et son associativité restent cohérentes avec les opérations de type intégrées.
  • Vous ne pouvez pas créer un opérateur sexuel en concaténant des correspondances qui n'existent pas dans la syntaxe : par ex.