[Classes et objets C++] Quelles sont les fonctions membres par défaut d'une classe ? (supérieur)

Table des matières

1. Les 6 fonctions membres par défaut de la classe

2. Constructeur (*^▽^*)

2.1 Conception

2.2 Caractéristiques

3. Destructeur (*^▽^*)

3.1 Conception

3.2 Caractéristiques

4. Copier le constructeur (*^▽^*)

4.1 Conception

4.2 Caractéristiques

5. Surcharge d'opérateur d'affectation (*^▽^*)

5.1 Surcharge de l'opérateur

5.2 Surcharge des opérateurs d'affectation


ヾ(๑╹◡╹)ノ" Les gens doivent toujours payer pour leur paresse passée !ヾ(๑╹◡╹)ノ"


1. Les 6 fonctions membres par défaut de la classe

S'il n'y a pas de membres dans une classe, on l'appelle simplement une classe vide.
Il n'y a rien dans la classe vide. Lorsqu'une classe n'écrit rien, le compilateur génère automatiquement les 6 fonctions membres par défaut suivantes.

 Fonction membre par défaut : la fonction membre générée par le compilateur sans implémentation explicite par l'utilisateur est appelée fonction membre par défaut.

2. Constructeur (*^▽^*)

2.1 Conception

Pour la classe de données :

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;
};
int main()
{
	Date d1;
	d1.Init(2022, 7, 5);//初始化
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);//初始化
	d2.Print();
	return 0;
}

Pour le code ci-dessus, vous pouvez définir la date de l'objet via la méthode publique Init et appeler cette méthode pour définir les informations chaque fois que l'objet est créé.

Le constructeur est une fonction membre spéciale portant le même nom que le nom de la classe . Il est automatiquement appelé par le compilateur lors de la création d'un objet de type classe pour s'assurer que chaque membre de données a une valeur initiale appropriée et n'est appelé qu'une seule fois dans tout le cycle de vie. de l'objet. .

2.2 Caractéristiques

Le constructeur est une fonction membre spéciale. Il convient de noter que bien que le nom du constructeur soit appelé construction, la tâche principale du constructeur n'est pas d'ouvrir de l'espace pour créer des objets, mais d'initialiser des objets .
fonctionnalité:
1. Le nom de la fonction est le même que le nom de la classe.
2. Aucune valeur de retour.
3. Le compilateur appelle automatiquement le constructeur correspondant lorsque l'objet est instancié .
4. Le constructeur peut être surchargé. [Cependant, la plupart de ce que nous écrivons est le constructeur par défaut, et généralement nous n'avons pas besoin de surcharge de fonction [écrire un constructeur par défaut + autre surcharge de fonction]]
class Date
{
public:
	// 1.无参构造函数
	Date()//构造函数:函数名与类名相同,无返回值
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestDate()
{
	Date d1; // 调用无参构造函数,注意,不能写成Date d1();
	Date d2(2015, 1, 1); // 调用带参的构造函数
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
}
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 aura défini explicitement le compilateur, il ne le générera plus . [C'est-à-dire que si le constructeur n'est pas implémenté, le compilateur C++ générera automatiquement un constructeur par défaut sans paramètres]

 Constructeur par défaut : (1) constructeur sans argument (2) constructeur par défaut complet (3) constructeur sans argument généré par le compilateur C++ [c'est-à-dire qu'il doit y avoir l'un des trois, s'il n'y a pas de constructeur par défaut] Le constructeur écrit n'est pas sans paramètres, ni tout par défaut】Il signalera une erreur]

6. Concernant la fonction membre par défaut générée par le compilateur, si le constructeur n'est pas implémenté, le compilateur générera un constructeur par défaut. Mais il semble que le constructeur par défaut ne sert à rien ? L'objet appelle le constructeur par défaut généré par le compilateur, mais l'objet _year/_month/_day est toujours une valeur aléatoire [le type intégré est toujours une valeur aléatoire] . Autrement dit , le constructeur par défaut généré par le compilateur ne sert à rien ici ?
Réponse : C++ divise les types en types intégrés ( types de base ) et en types personnalisés. Le type intégré est le type de données fourni par le langage, tel que : int/char..., et le type personnalisé est le type que nous définissons nous-mêmes en utilisant class/struct/union . Lorsqu'aucun constructeur n'est implémenté, le compilateur génère un constructeur par défaut qui appelle sa fonction membre par défaut sur les membres du type personnalisé .

Si les membres d'une classe sont tous des types personnalisés, nous pouvons utiliser les fonctions générées par défaut. S'il existe des membres de fonction de type intégrés ou si vous devez afficher l'initialisation des paramètres, vous devez implémenter le constructeur vous-même. (vous devez passer des paramètres pour initialiser, vous devez implémenter le constructeur vous-même) [la plupart d'entre eux écrivent des constructeurs par eux-mêmes]

Le constructeur par défaut généré par le compilateur C++ ne gère pas les variables de membre de fonction de type intégré et le membre de type personnalisé appellera son propre constructeur par défaut.

Remarque : dans C++11 , un correctif a été corrigé pour le défaut selon lequel les membres de type intégrés ne sont pas initialisés, c'est-à-dire que les variables membres de type intégré peuvent recevoir des valeurs par défaut lorsqu'elles sont déclarées dans une classe . [Remarque : il s'agit d'une déclaration, il s'agit donc d'une valeur par défaut, pas d'une initialisation]
7. Le constructeur sans paramètre et le constructeur par défaut sont appelés constructeurs par défaut et il ne peut y avoir qu'un seul constructeur par défaut.
Remarque : les constructeurs sans argument, les constructeurs par défaut complets et les constructeurs que nous n'avons pas écrits pour être générés par le compilateur par défaut peuvent tous être considérés comme des constructeurs par défaut.
class Date
{
public:
	Date()//无参的构造函数
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)//全缺省的构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

void Test()
{
	Date d1;//这里会发生错误,默认构造函数只能有一个
}

Des erreurs se produisent lors de l'exécution et des conflits se produisent.

En général, une classe C++ doit écrire son propre constructeur. Il n'y a que de rares cas où vous pouvez laisser le compilateur générer automatiquement :

(1) Les membres de la classe sont tous des membres de type auto-définis, et ces membres fournissent des constructeurs par défaut.

(2) La valeur par défaut est donnée lorsque le membre de type intégré est déclaré.

3. Destructeur (*^▽^*)

3.1 Conception

Destructeur : Contrairement à la fonction du constructeur, le destructeur ne complète pas la destruction de l'objet , et la destruction locale de l'objet est faite par le compilateur. Lorsque l'objet est détruit, il appellera automatiquement le destructeur pour effectuer un travail de nettoyage des ressources de la classe. 【histoire】

3.2 Caractéristiques

Les destructeurs sont des fonctions membres spéciales.
fonctionnalité:
1. Le nom du destructeur est le caractère ~ avant le nom de la classe .
2. Aucun paramètre et aucune valeur de retour.
3. Une classe a un et un seul destructeur. S'il n'est pas explicitement défini, le système générera automatiquement un destructeur par défaut [les variables des types personnalisés appelleront leurs propres destructeurs et les variables des types intégrés ne seront pas traitées]. Remarque : les destructeurs ne peuvent pas être surchargés
4. Lorsque le cycle de vie de l'objet se termine, le système de compilation C++ appelle automatiquement le destructeur.
5.  En ce qui concerne le destructeur généré automatiquement par le compilateur, le destructeur par défaut généré par le compilateur appelle son propre destructeur pour les membres de type personnalisé.
6. S'il n'y a pas d'application de ressource dans la classe, le destructeur ne peut pas être écrit, et le destructeur par défaut généré par le compilateur est utilisé directement, comme la classe Date ; lorsqu'il y a une application de ressource, elle doit être écrite, sinon cela entraînera une fuite de ressources, comme la classe Stack [En bref, lorsqu'il y a une application de ressources, le destructeur doit être écrit].

4. Copier le constructeur (*^▽^*)

4.1 Conception

 Lorsqu'un objet est créé, un nouvel objet est créé qui est exactement le même qu'un objet.

Constructeur de copie : il n'y a qu'un seul paramètre formel , qui est une référence à l' objet de ce type de classe ( généralement la décoration const est couramment utilisée ) , et est automatiquement appelé par le compilateur lors de la création d'un nouvel objet avec un objet de type classe existant .

4.2 Caractéristiques

1. Le constructeur de copie est une forme surchargée du constructeur .

2. Le constructeur de copie n'a qu'un seul paramètre et doit être passé par référence , et la méthode de passage par valeur provoquera des appels récursifs infinis .
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)//拷贝构造函数
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

Méthode de transmission de valeur : appeler le constructeur de copie appellera le constructeur de copie. Pour appeler cette fonction, vous devez d'abord transmettre des paramètres. La transmission de paramètres est une copie temporaire . À ce stade, l'état est un objet de copie temporaire, ce qui équivaut à copie de construction, et vous devez appeler la fonction de construction de copie, un appel récursif infini sera déclenché à ce moment.

Passage de paramètres par référence : la construction de la copie ne sera pas appelée.

[Un objet initialise un autre objet de cette classe, qui est une construction de copie] [Copier un objet est une construction de copie]

Pour les objets de type personnalisé, l'initialisation de la copie doit être effectuée en appelant le constructeur de copie.

Si aucune définition n'est affichée, le système génère un constructeur de copie par défaut. L' objet constructeur de copie par défaut est copié dans l'ordre des octets en fonction du stockage en mémoire. Ce type de copie est appelé copie superficielle ou copie de valeur. [C'est-à-dire que les membres du type intégré termineront la copie de valeur (copie superficielle) et les membres du type personnalisé appelleront la construction de copie de ce membre]
Conclusion : pour les classes générales, la structure de copie auto-générée est suffisante, et il n'est pas nécessaire d'écrire la structure de copie vous-même. Mais pour les classes comme stack, qui gèrent les ressources directement par elles-mêmes, elles doivent implémenter elles-mêmes la copie en profondeur.
4.  Ensuite, le constructeur de copie par défaut généré par le compilateur peut déjà compléter la valeur copy de byte order , devons-nous encore l'implémenter nous-mêmes ? Bien sûr, des classes comme la classe Date ne sont pas nécessaires. Les classes comme les piles ne sont pas autorisées. [Il y a une adresse dans la pile, puis les deux piles iront au même espace, et elles s'affecteront lors de l'ajout, de la suppression, de la vérification et de la modification. Lors de la destruction, le même espace sera détruit deux fois, et le même espace sera publié deux fois. Le programme Il s'effondrera - la solution finale, réalisez la construction de copie par vous-même - copie profonde]
S'il n'y a pas d'application de ressource impliquée dans la classe, le constructeur de copie peut être écrit ou non ; une fois que l'application de ressource est impliquée, le constructeur de copie doit être écrit, sinon il s'agit d'une copie superficielle.

 Afin d'améliorer l'efficacité du programme, lors du passage des paramètres aux objets généraux, essayez d'utiliser le type de référence autant que possible et utilisez les références autant que possible en fonction de la scène réelle lors du retour.

5. Surcharge d'opérateur d'affectation (*^▽^*)

5.1 Surcharge de l'opérateur

C++ introduit la surcharge d'opérateurs pour améliorer la lisibilité du code . La surcharge d'opérateurs est une fonction avec un nom de fonction spécial et a également son type de valeur de retour, son nom de fonction et sa liste de paramètres. Le type de valeur de retour et la liste de paramètres sont similaires aux fonctions ordinaires .
Le nom de la fonction est : le mot clé operator suivi du symbole de l'opérateur qui doit être surchargé .
Prototype de fonction :  opérateur de type valeur de retour opérateur ( liste de paramètres )
Valeur de retour : le résultat de l'opération
Paramètres : opérandes d'opération
Avis:
(1) De nouveaux opérateurs ne peuvent pas être créés en connectant d'autres symboles : tels que operator@
(2) L'opérateur surchargé doit avoir un paramètre de type de classe
(3) La signification de l'opérateur utilisé pour les types intégrés ne peut pas être modifiée, par exemple : l'entier intégré + ne peut pas modifier sa signification
(4) Lorsqu'il est surchargé en tant que fonction membre de classe, ses paramètres formels semblent être inférieurs de 1 au nombre d'opérandes , car le premier paramètre de la fonction membre est le caché this
(5) .*     :: sizeof ?: . Notez que les 5 opérateurs ci-dessus ne peuvent pas être surchargés. Cela revient souvent dans les examens écrits.             
//日期的判断是否相等
bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year 
		&& d1._month == d2._month 
		&& d1._day == d2._day;
 }
//放到类里面 Date,此时是在类外面的写法
//1.
if (operator==(d1, d2))
{
	cout << "==" << endl;
}
//2/
if (d1 == d2)
{
	cout << "==" << endl;
}
//1.和2.是等价的,编译器会处理成1.

(1) Les types intégrés peuvent utiliser directement divers opérateurs, mais les types personnalisés ne peuvent pas utiliser directement divers algorithmes. Afin de permettre l'utilisation de divers opérateurs par des types personnalisés, il existe une surcharge d'opérateurs.

(2) Il y a autant de paramètres de fonction que d'opérandes. (== deux opérandes ; ++ un opérande ;)

Ecrit en classe :

//日期的判断是否相等
bool operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
//放到类里面 Date,此时是在类里面的写法
//1.
if (d1.operator==(d2))
{
	cout << "==" << endl;
}
//2/
if (d1 == d2)
{
	cout << "==" << endl;
}
//1.和2.是等价的,编译器会处理成对应重载运算法调用

Si les deux codes existent (le compilateur peut passer car il est conforme à la surcharge de fonction), le compilateur utilisera d'abord l'opérateur surchargeant dans la classe.

//判断日期小
bool operator<(const Date& d)
{
	//小的情况
	if (_year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day))
	{
		return true;
	}
	else
	{
		return false;
	}
}

Attention aux détails ici, à ne pas prendre pour acquis.

5.2 Surcharge des opérateurs d'affectation

1. Type de paramètre
2. valeur de retour
3. Vérifiez si vous vous attribuez une valeur
4. retourner *ceci
5. Si une classe ne définit pas explicitement une surcharge d'opérateur d'affectation, le compilateur en générera également une pour terminer la copie de la valeur ordonnée par octet de l'objet
1. Format de surcharge de l'opérateur d'affectation
Type de paramètre : const T& , le passage par référence peut améliorer l'efficacité du passage des paramètres
Type de valeur de retour  : T& , la référence de retour peut améliorer l'efficacité du retour, et le but de la valeur de retour est de prendre en charge l'affectation continue
Vérifiez si vous vous attribuez une valeur
Return *this  : pour aggraver le sens de l'affectation continue
//d2 = d1;-> d2.operator(&d2, d1)
Date& operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}
int main()
{

	Date d1(2000, 8, 20);
	Date d2(2000, 9, 17);
	Date d3(d1);//拷贝构造,一个存在的对象去初始化另一个要创建的对象
	d2 = d1;//赋值重载(复制拷贝) 两个已经存在的对象之间赋值
}
2. L'opérateur d'affectation ne peut être surchargé qu'en tant que fonction membre d'une classe et ne peut pas être surchargé en tant que fonction globale
L'opérateur d'affectation est surchargé dans une fonction globale. Notez qu'il n'y a pas ce pointeur lors de la surcharge dans une fonction globale. Deux paramètres sont requis, mais
Si l'opérateur d'affectation n'est pas explicitement implémenté, le compilateur en générera un par défaut. A ce moment, l'utilisateur implémente un global
La surcharge de l'opérateur d'affectation est en conflit avec la surcharge de l'opérateur d'affectation par défaut générée par le compilateur dans la classe, de sorte que la surcharge de l'opérateur d'affectation ne peut être qu'une fonction membre de la classe .
3. Lorsque l'utilisateur n'implémente pas explicitement, le compilateur génère une surcharge d'opérateur d'affectation par défaut, qui est copiée octet par octet sous la forme value . Remarque : les variables de membre de type intégré sont directement affectées, tandis que les variables de membre de type personnalisé doivent appeler la surcharge de l'opérateur d'affectation de la classe correspondante pour terminer l'affectation.
4. La fonction de surcharge d'affectation par défaut générée par le compilateur peut déjà copier la valeur de byte order , devons-nous encore l'implémenter nous-mêmes ? Bien sûr, des classes comme la classe Date ne sont pas nécessaires. Si la gestion des ressources n'est pas impliquée dans la classe, peu importe si l'opérateur d'affectation est implémenté ; une fois que la gestion des ressources est impliquée, elle doit être implémentée.

Connaissance supplémentaire : Le pointeur nul n'existe-t-il pas ? existe et une adresse vide est une adresse existante.

Je suppose que tu aimes

Origine blog.csdn.net/m0_57388581/article/details/132179408
conseillé
Classement