2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Dans le chapitre précédent, nous avons brièvement évoqué certains concepts de classes et d’objets. Passons maintenant en revue ensemble.
class est le mot-clé de classe de définition, suivi du nom de la classe de définition, {} est le corps principal de la classe, suivi de ";", et les fonctions membres définies dans la classe sont développées par défaut (en ligne).
Un format d'encapsulation, divisé en trois types : public, privé et protégé. La portée des autorisations d'accès commence à partir de la position où le qualificatif 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, il utilisera le domaine. se termine par } c'est-à-dire la 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 de portée :: pour indiquer à quel domaine de classe appartient le membre.
Voici le long métrage ///
• Le processus de création d'un objet dans la mémoire physique à l'aide d'un type de classe est appelé instanciation de classe.
• Une classe est une description abstraite d'un objet. C'est comme un modèle. Elle limite les variables membres de la classe uniquement.
Il s'agit d'une déclaration et aucun espace n'est alloué. L'espace ne sera alloué que lorsqu'un objet est instancié à l'aide d'une classe.
• Une classe peut instancier plusieurs objets. Les objets instanciés occupent l'espace physique réel et stockent les variables membres de la classe.
Pour résumer en une phrase : ce n'est que lorsque les objets sont instanciés avec des classes qu'ils alloueront de l'espace (tout comme construire une maison avec des dessins occupera de l'espace).
- class Date
- {
- public:
- void Init(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
Dans le code ci-dessus, les variables membres en privé sont uniquement définies et aucun nouvel espace n'est ouvert et ;
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
Ensuite, deux nouveaux objets sont instanciés, donc un nouvel espace s'ouvre.
• Le premier membre est à l'adresse décalée 0 de la structure.
• Les autres variables membres doivent être mappées à des adresses qui sont des multiples entiers d'un certain nombre (logarithme).
• Remarque : logarithme = le plus petit du logarithme par défaut du compilateur et de la taille du membre.
• Le logarithme par défaut dans VS est 8.
• La taille totale de la structure est : un multiple entier du nombre maximum par paire (le plus grand de tous les types de variables et le plus petit paramètre d'appariement par défaut).
• Si une structure est imbriquée, la structure imbriquée s'aligne sur un multiple entier de son logarithme maximum et de la taille globale de la structure.
Il s'agit d'un multiple entier de tous les plus grands logarithmes (y compris les logarithmes des structures imbriquées).
C'est ce que nous savons en langage C. Prenons comme exemple les questions suivantes :
- // 计算⼀下A实例化的对象是多⼤?
- class A
- {
- public:
- void Print()
- {
- cout << _ch << endl;
- }
- private:
- char _ch;
- int _i;
- };
Aucune fonction n'est appelée, alors regardez simplement les variables membres. _ch est un type de caractère char, avec une taille de 1 octet ; _i est un type int, avec une taille de 4 octets. Le plus grand espace occupé par tous les membres est le. numéro d'alignement, il s'agit donc de _ Mettez ch à la position avec l'indice 0, laissez trois espaces vides, puis mettez _i Il occupe un total de 8 octets d'espace de 0 à 7, ce qui est un multiple entier du numéro d'alignement maximum. . Il remplit les conditions, donc la réponse à cette question est 8 .
Et si c’était ces deux questions ?
- class B
- {
- public:
- void Print()
- {
- //...
- }
- };
-
- class C
- {
- };
Nous pouvons constater qu’il y a une fonction vide dans la classe B et aucun membre dans la classe C. En langage C, nous faisons en sorte que les deux classes occupent 1 octet d’espace.
• DIl y a deux fonctions membres, Init et Print, dans la classe ate. Il n'y a aucune distinction entre les différents objets dans le corps de la fonction. Ensuite, lorsque d1 appelle Init et Print,
Lors de l'impression de la fonction, comment la fonction sait-elle si elle doit accéder à l'objet d1 ou à l'objet d2 ?Alors ici nous verrons que C++ donne
Un pointeur this implicite résout le problème ici
• Après la compilation du compilateur, les fonctions membres de la classe ajouteront par défaut un pointeur du type de classe actuel, appelé ce pointeur, à la première position du paramètre formel. Par exemple, le véritable prototype de la classe Init of Date est void Init(Date* const this, int year,
int mois, int jour)
• Lors de l'accès aux variables membres dans les fonctions membres d'une classe, elles sont essentiellement accessibles via le pointeur this. Par exemple, lors de l'attribution d'une valeur à _year dans la fonction Init, this-.
>_année = année;
• C++ stipule que ce pointeur ne peut pas être écrit explicitement à la place des paramètres réels et des paramètres formels (le compilateur gérera cela lors de la compilation), mais ce pointeur peut être utilisé explicitement dans le corps de la fonction.
Par exemple
- class Date
- {
- public:
- // void Init(Date* const this, int year, int month, int day)
- void Init(int year, int month, int day)
- {
- // 编译报错:error C2106: “=”: 左操作数必须为左值
- // this = nullptr;
- // this->_year = year;
- _year = year;
- this->_month = month;
- this->_day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
-
- int main()
- {
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
- // d1.Init(&d1, 2024, 3, 31);this指针
- d1.Init(2024, 3, 31);
-
- // d1.Init(&d2, 2024, 7, 5);
- d2.Init(2024, 7, 5);
- //不能自己写
- return 0;
- }
Pour l'écriture des paramètres réels dans la fonction Init, C++ ajoutera une constante invisible ce pointeur (qui ne peut pas être modifié) vers la première variable de la fonction, comme indiqué dans les commentaires de la fonction.
Tant que le pointeur nul n'est pas déréférencé, aucune erreur ne sera signalée.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Bien que p se voit attribuer un pointeur nul et pointe vers la fonction Print, il n'est pas déréférencé, donc aucune erreur ne sera signalée ici. Et ce programme :
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- cout << _a << endl;//这个地方多了一句
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Par rapport au programme précédent, il n'y a qu'une seule phrase supplémentaire cout << _a << endl, mais à ce moment le pointeur nul pointe vers une variable membre, et le pointeur nul sera déréférencé, donc le programme signalera une erreur.
La fonction membre par défaut est une fonction membre qui n'est pas explicitement implémentée par l'utilisateur et qui est automatiquement générée par le compilateur. Elle est appelée fonction membre par défaut. Pour une classe, si on ne l'écrit pas, le compilateur générera par défaut les 6 fonctions membres par défaut suivantes, qui sont les 6 suivantes
Le constructeur est une fonction membre spéciale. Il convient de noter que bien que le nom du constructeur soit appelé constructeur, la tâche principale du constructeur n'est pas d'ouvrir de l'espace pour créer des objets (l'objet local que nous utilisons souvent est l'espace lorsque la pile frame est créé) ), à la place, l'objet est initialisé lorsque l'objet est instancié. L'essentiel est de remplacer la fonction d'initialisation Init.
1. Le nom de la fonction est le même que le nom de la classe.
2. Aucune valeur de retour. (Pas besoin d'écrire vide)
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 explicitement 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 l'aura défini explicitement, le compilateur ne le générera plus.
Les cinq premiers éléments sont relativement simples, donnons un exemple :
- class Date
- {
- public:
- //1.⽆参构造函数,无需写void
- Date()//若不写,则构造函数会写出这一中无参构造函数
- {
- _year = 1;
- _month = 1;
- _day = 1;
- }
- //2.全缺省构造函数
- Date(int year = 1, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- private:
- int _year;
- int _month;
- int _day;
- }
-
- int main()
- {
- Date d1;//此时相当于已经自动调用了Init函数
- Date d2;
-
- return 0;
- }
Deux constructeurs sont écrits dans le programme, l'un est un constructeur sans paramètre et l'autre est un constructeur par défaut complet. Lorsque nous n'écrivons pas de fonction, le programme écrira jusqu'à un constructeur sans paramètre. deux fonctions sont des fonctions surchargées, en raison de l'ambiguïté d'appel, les deux fonctions ne peuvent pas exister en même temps.
6. Les constructeurs sans paramètres, les constructeurs par défaut complets et les constructeurs générés par le compilateur par défaut lorsque nous n'écrivons pas de constructeur sont tous appelés constructeurs par défaut. Or, une et une seule de ces trois fonctions existe et ne peut exister 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. Il convient de noter que de nombreux étudiants pensent que le constructeur par défaut est le constructeur par défaut généré par le compilateur. En fait, le constructeur sans paramètre et le constructeur par défaut complet sont également des constructeurs par défaut. Pour résumer, ce sont des constructeurs qui peuvent être appelés sans passer de réels. paramètres. C'est ce qu'on appelle la construction par défaut.
La sixième fonctionnalité est plus compliquée : les constructeurs sans paramètres, les constructeurs par défaut et les constructeurs générés par le compilateur par défaut lorsque nous n'écrivons pas de constructeur sont tous appelés constructeurs par défaut, et il ne peut y en avoir qu'un. comme constructeur généré par le compilateur par défaut !
7. Si nous ne l'écrivons pas, le constructeur généré par défaut par le compilateur n'a aucune exigence pour l'initialisation des variables membres de type intégrées. En d'autres termes, qu'il soit initialisé ou non est incertain, cela dépend du compilateur. . 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 résoudre le problème. Nous expliquerons la liste d'initialisation en détail dans le chapitre suivant. .
La fonction du destructeur est opposée à celle du constructeur. Le destructeur ne termine pas la destruction de l'objet lui-même. Par exemple, l'objet local a un cadre de pile lorsque la fonction se termine et que le cadre de pile est détruit. libéré.Nous n'avons pas besoin de le contrôler.C++ stipule que les objets Une fois détruits, le destructeur sera automatiquement appelé pour terminer le nettoyage et la libération des ressources dans l'objet. La fonction du destructeur est similaire à la fonction Destroy que nous avons implémentée auparavant dans Stack. Par exemple, Date n'a pas de destruction. En fait, il n'y a pas de ressources à libérer. Donc à proprement parler, Date n'a pas besoin de destructeur. destructor est similaire à la fonction Destroy. Il doit être utilisé pour les structures de données qui nécessitent des objets non locaux tels que des piles et des files d'attente pour demander des ressources.
1. Le nom du destructeur est précédé du caractère ~ avant le nom de la classe. (Similaire à la négation au niveau du bit en langage C)
2. Aucun paramètre et aucune valeur de retour. (Ceci est similaire à la structure, et il n'est pas nécessaire d'ajouter du vide)
3. Une classe ne peut avoir qu’un seul destructeur. S'il n'est pas explicitement défini, le système générera automatiquement un destructeur par défaut.
4. Lorsque le cycle de vie de l'objet se termine, le système appelle automatiquement le destructeur.
5. Semblable au constructeur, si nous n'écrivons pas un destructeur généré automatiquement par le compilateur, il ne traitera pas les membres de type intégrés qui appelleront leurs destructeurs.
6. Il convient également de noter que lorsque nous écrivons explicitement le destructeur, le destructeur du membre de type personnalisé sera également appelé, c'est-à-dire que le destructeur du membre de type personnalisé sera automatiquement appelé quelle que soit la situation.
7. S'il n'y a aucune ressource demandée dans la classe, le destructeur n'a pas besoin d'être écrit et le destructeur par défaut généré par le compilateur, tel que Date, peut être utilisé directement. Si le destructeur généré par défaut peut être utilisé, il n'est pas nécessaire de l'afficher. Écrivez le destructeur, comme MyQueue ; mais lorsqu'il existe une application de ressources, vous devez écrire le destructeur vous-même, sinon cela entraînera une fuite de ressources, comme Stack.
8. Pour plusieurs objets dans un domaine local, C++ stipule que ceux définis ultérieurement seront détruits en premier.
Identique au constructeur ci-dessus, le destructeur par défaut généré automatiquement par C++ ne nous sera généralement pas d'une grande aide, mais il est plus pratique dans certains cas rares, comme l'utilisation de deux piles pour implémenter une file d'attente :
- public:
- Stack(int n = 4)//相当于Init
- {
- _a = (STDataType*)malloc(sizeof(STDataType) * n);
- if (nullptr == _a)
- {
- perror("malloc fail!");
- return;
- }
- _capacity = n;
- _top = 0;
- }
- ~Stack()//相当于Destroy
- {
- cout << "~Stack()" << endl;
- free(_a);
- _a = nullptr;
- _top = _capacity = 0;
- }
- private:
- STDataType* _a;
- size_t _capacity;
- size_t _top;
- };
-
- class MyQueue
- {
- public:
- //编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
- // 显⽰写析构,也会⾃动调⽤Stack的析构
-
- private:
- Stack pushst;
- Stack popst;
- };
A ce moment, le compilateur de MyQueue appelle automatiquement le constructeur (initialisation) et le destructeur (destruction) de la pile.