[C++] Classes et objets (3) Surcharge d'opérateurs Surcharge d'affectations Adresse et Const Adressage Surcharge d'opérateurs

avant-propos

Dans ce chapitre, nous reprenons le chapitre précédent pour continuer à comprendre les fonctions membres par défaut des classes, la surcharge d'affectation , la surcharge d'adresse et la surcharge d'opérateur d'adresse const.

Mais avant de parler des trois fonctions membres par défaut restantes, nous devons d'abord comprendre la surcharge d'opérateur . , car la surcharge d'affectation, la surcharge de prise d'adresse et la surcharge d'opérateur de prise d'adresse const font en fait partie de la surcharge d'opérateur.


1. Surcharge de l'opérateur

1. Le concept de surcharge d'opérateur

Pour les types intégrés de C++, nous avons de nombreux opérateurs qui peuvent être utilisés, mais ces opérateurs ne peuvent pas être utilisés pour les types personnalisés. Nous pouvons seulement écrire une fonction similaire à la fonction de l'opérateur et laisser le type personnalisé l'appeler.
Par exemple:

#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	//给一个给日期加num天,不修改原始值
	Date Add(int num)
	{
    
    
		//......
	}
	//给一个日期加num天,并修改原始值
	Date AddEqual(int num)
	{
    
    
		//.....
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	int a = 10;
	a + 10;
	a += 10;
	Date d1;
	d1.Add(10);
	//d1+10;    //想写成这样,这样更直观方便,可是编译器不允许啊啊啊
	d1.AddEqual(10);
	//d1+=10;   //想写成这样,这样更直观方便,可是编译器不允许啊啊啊
	return 0;
}

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 d'opérateur de type valeur de retour (liste de paramètres)

Il n'est pas facile de comprendre la surcharge d'opérateur simplement en regardant la définition. Regardons d'abord le code, et comprenons la définition et les points d'attention tout en analysant le code.

// 全局的operator==
#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
//private:
	int _year;
	int _month;
	int _day;
};
// 这里会发现:运算符重载成全局的 就需要成员变量是公有的,不然下面的函数无法访问到成员变量,
// 那么问题来了,封装性如何保证?
//这点我们现在还没有办法解决,所以我们先暂时将成员设定为公有。
//还有一种办法就是把它写进类中,变成成员函数。(暂时不用此种方法)

//判断两个对象是否相同
bool operator==(const Date& d1, const Date& d2)
{
    
    
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

void Test()
{
    
    
	Date d1(2023, 2, 12);
	Date d2(2023, 2, 12);
	cout << operator==(d1, d2) << endl;//判断两个对象是否相同,第一种使用方法,直接调用函数
	cout << (d1 == d2) << endl;//判断两个对象是否相同,第二种使用方法,使用重载后的运算符
	//此处必须加括号,运算符优先级:<< 大于 ==
}

Je crois qu'après avoir lu attentivement ce code, vous avez déjà une certaine compréhension de la surcharge d'opérateur, c'est-à-dire que l'opérateur qui est à peu près équivalent à un type personnalisé est en fait une fonction que nous écrivons manuellement, mais après la surcharge de l'opérateur, nous pouvons utiliser construit Opérateurs de type -in comme Utilisez la fonction de cette façon.

2. Précautions en cas de surcharge de l'opérateur

  • De nouveaux opérateurs ne peuvent pas être créés en concaténant d'autres symboles (opérateurs qui n'existent pas en C++) : tels que operator@
  • Les opérateurs surchargés doivent avoir un paramètre de type de classe (car la surcharge d'opérateurs consiste principalement à permettre aux types personnalisés d'utiliser des opérateurs comme les types intégrés)
  • La signification des opérateurs utilisés pour les types intégrés ne peut pas être modifiée , par exemple : l'entier intégré + ne peut pas modifier sa signification (car la surcharge d'opérateurs consiste principalement à permettre aux types personnalisés d'utiliser des opérateurs tels que les types intégrés, les types intégrés n'ont pas besoin de surcharge de l'opérateur)
  • 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 masquéthis
  • .* :: sizeof ?: .Notez que les 5 opérateurs ci-dessus ne peuvent pas être surchargés. Cela apparaît souvent dans les questions écrites à choix multiples.

Deuxièmement, le cas particulier de la surcharge des opérateurs

1. Types avant ++ et post ++

Après l'explication ci-dessus de la surcharge d'opérateur, je pense que vous pouvez également écrire sa surcharge d'opérateur pour certains autres opérateurs de la classe Date, tels que :

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator+(int num)
	{
    
    
		//......
	}
	Date& operator-(Date& d)
	{
    
    
		//......
	}
	Date& operator+=(int num)
	{
    
    
		//......
	}
	//......
	//其实这么多运算符重载我们可以一一实现,也可以实现一到两个,然后让其他运算符重载
private:
	int _year;
	int _month;
	int _day;
};

Mais lorsque nous apprenions le langage C, nous apprenions pré ++, post ++, pré - -, post - -, que devrions-nous faire pour ce type de surcharge d'opérateurs ?
Commençons par écrire la liste des paramètres en dehors de la fonction :

//前置++
	Date& operator++()
	{
    
    

	}
//后置++
	Date operator++()
	{
    
    

	}

Nous avons constaté que leurs listes de paramètres externes de fonction sont exactement les mêmes, de sorte que la surcharge de fonction ne peut pas du tout être constituée, et nous ne pouvons en implémenter qu'une parmi pré-++ et post-++.
Afin de résoudre ce problème, nous spécialisons en C++ ce type de préposition ++, postposition ++, préposition - -, postposition - - lorsque ce type d'opérateur est surchargé. Les règles sont :

  1. Prepend++ est implémenté normalement
  2. Postfix ++ passe un paramètre int supplémentaire lorsque l'opérateur est surchargé (ce paramètre est uniquement pour un espace réservé, pour obtenir une surcharge de fonction, il n'a pas d'autre effet, et il n'est pas nécessaire de vraiment passer des paramètres)

(- - similaire aux règles ci-dessus)

Donc, pour l'implémenter correctement, nous devrions l'implémenter comme ceci:

//前置++
	Date& operator++()
	{
    
    
		*this += 1;//假设 +=我们已经运算符重载过了
		return this;
	}
//后置++
	Date operator++(int a)//形参名可以不写,直接写成 int,调用时也不需要传递参数
	{
    
    
		Date tmp(this); //会去调用拷贝构造 
		*this += 1; //假设 +=我们已经运算符重载过了
		return tmp;
	}

2. Opérateur d'insertion de flux << extraction de flux >>

Lors de l'explication de la surcharge d'opérateur, nous avons dit que << est en fait un opérateur de décalage , mais il est surchargé en C++ en tant qu'opérateur d'insertion de flux, alors comment faites-vous ? Maintenant que nous avons appris la surcharge des opérateurs, nous pouvons discuter de ce problème.

coutTout d'abord, les et que nous utilisons souvent cinsont en fait respectivement un ostreamobjet de type et un objet de type. Ces deux types se trouvent respectivement istreamdans l'espace de noms standard des deux bibliothèques standard C++ , puis la bibliothèque standard C++ que nous utilisons souvent contient et fichiers d'en-tête, Ainsi, lorsque nous utilisons ou , nous devons inclure les fichiers d'en-tête et étendre le contenu de l'espace de noms standard au global.<ostream><istream>std<iostream><istream><ostream>cincout<iostream>using namespeace std;

insérez la description de l'image ici
Reprenons la <<surcharge de l'opérateur d'extraction de flux
insérez la description de l'image ici

<<On peut voir qu'il existe de nombreuses surcharges d'opérateurs + surcharges de fonctions pour les opérateurs d'extraction de flux en C++ .Nous pouvons utiliser des types intégrés à volonté <<, mais nous n'avons aucun moyen d'utiliser des types personnalisés, car les types personnalisés sont les nôtres et C++ ne peut pas Prédire quel type personnalisé nous voulons écrire, puis effectuer une surcharge d'opérateur pour notre type personnalisé, nous voulons donc que notre type personnalisé soit extrait avec des flux, nous devons donc implémenter manuellement la surcharge d'opérateur du <<type personnalisé . Voyons la première façon d'écrire<<
insérez la description de l'image ici


//<< 运算符重载
#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	ostream& operator<< (ostream&out)
	{
    
    
		out << _year << "年" << _month << "月" << _day << "日" << endl;
		return out;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1;
	int a = 10;
	cout << a << endl;//C++对自定义类型已经实现了 << 的运算符重载
	cout << d1;  
	return 0;
}

insérez la description de l'image ici
Échec de la compilation. Après vérification, nous avons constaté que nous avions écrit la 67e ligne à l'envers ! doit être écrit comme

d1 << cout;           //第一种写法
d1.operator<<(cout);  //第二种写法

Parce que lorsque l'opérateur est surchargé, le premier paramètre est l'opérande gauche et le deuxième opérande est l'opérande droit (notez que le premier paramètre est un thispointeur caché)

Après l'avoir écrit correctement, exécutons-le :

insérez la description de l'image ici
Il n'y a pas de problème, mais c'est trop pervers pour écrire comme ça, et ça viole notre intuition de l'utiliser. L'intuition nous dit que nous devrions l'utiliser comme ça :

cout << d1;

Ensuite, nous devrions insérer ce flux <<dans la définition surchargée en dehors de la fonction, car le premier paramètre passé par défaut à l'intérieur de la classe est thisun pointeur, et nous n'atteindrons jamais notre objectif.
On définit donc à l'extérieur :

#include<iostream>
using namespace std;
class Date
{
    
    
	friend ostream& operator<<(ostream& out, Date& d);//友元,允许我们在类外部使用成员变量。
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& out, Date& d)
{
    
    
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

int main()
{
    
    
	Date d1;
	int a = 10;
	cout << a << endl;//C++对自定义类型已经实现了 << 的运算符重载
	cout << d1;
	return 0;
}

Une autre question, <<pourquoi utilisons-nous des références lorsque nous écrivons la surcharge d'opérateurs ?
insérez la description de l'image ici
Remarque : <<il est laissé associatif !
La valeur de retour est référencée et nous pouvons réaliser une impression continue de plusieurs types personnalisés !

//假如d1 d2 d3 都是Date类型
cout << d1 << d2 << d3 << endl;

4. Surcharge de l'opérateur d'affectation (fonction membre par défaut)

1. Introduction

Examinons d'abord un scénario d'utilisation. Et si nous voulons affecter les données d' un type personnalisé initialisé à un autre type personnalisé initialisé (pas l'affectation lorsque l'objet est initialisé, et l'affectation lorsque l'objet est initialisé utilise la construction de copie). ?
Jetez un oeil au code ci-dessous:

//赋值重载
#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << _year << "年" << _month << "月" << _month << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1;
	Date d2 = d1;//或者Date d2(d1)  会调用默认生成的拷贝构造,对象初始化时赋值用的是拷贝构造
	Date d3;
	d3 = d1;//我们没有实现Date类的运算符 = 的赋值重载,所以会调用默认生成的赋值重载
	        //最后d3里面的数据与d1一样
}

2. Caractéristiques

1. 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 forme de valeur.

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 d'opérateur d'affectation (=) de la classe correspondante pour terminer l'affectation.
Exemple de code :
tel que le code ci-dessus

2. Format de surcharge de l'opérateur d'affectation :

Type de paramètre : const T& , le passage de la référence peut améliorer l'efficacité du passage du paramètre
Type de valeur de retour : T& , le retour de la référence peut améliorer l'efficacité du retour, le but du retour de la valeur est de prendre en charge l'affectation continue pour détecter s'il faut s'attribuer lui-même return
*this : se conformer à l'affectation continue le sens de

#include<iostream>
using namespace std;
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;
	}
	//自己写的 赋值重载
	Date& operator=(const Date& d)
	{
    
    
		if (this != &d)
		{
    
    
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1(2023,2,12);
	Date d2;
	d2 = d1;
	return 0;
}

insérez la description de l'image ici
insérez la description de l'image ici

3. Les opérateurs d'affectation ne peuvent être surchargés qu'en tant que fonctions membres de classes et ne peuvent pas être surchargés en tant que fonctions globales

#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
    
    
	if (&left != &right)
	{
    
    
		left._year = right._year;
		left._month = right._month;
		left._day = right._day;
	}
	return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

insérez la description de l'image ici


原因:赋值运算符如果不显式实现,编译器会生成一个默认的。
此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数

insérez la description de l'image ici

4. Si la gestion des ressources n'est pas impliquée dans la classe, l'opérateur d'affectation peut être implémenté ou non, une fois que la gestion des ressources est impliquée, elle doit être implémentée.

Comme le constructeur de copie, continuons à réfléchir : étant donné que la fonction de surcharge de l'opérateur d'affectation par défaut générée par le compilateur peut déjà compléter la copie de valeur de l'ordre des octets, devons-nous l'implémenter nous-mêmes pour les types intégrés ?
Comme la construction de copie, la caractéristique 4 est également la condition de jugement pour que nous écrivions ou non la fonction de surcharge de copie !
Par exemple:

// 这里会发现下面的程序会崩溃掉,编译器生成的是浅拷贝,导致我们析构了两次空间,
//这里就需要我们以后讲的深拷贝去解决。
#include<iostream>
using namespace std;
typedef int DataType;
class Stack
{
    
    
public:
	Stack(size_t capacity = 10)
	{
    
    
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
    
    
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
    
    
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2;
	s2 = s1;
	return 0;
}

insérez la description de l'image ici
Ici, nous avons terminé la quatrième des six fonctions membres par défaut : la surcharge de copie .
La surcharge de copie fait en fait partie de la surcharge d'opérateur !

5. Take address et const take address surcharge de l'opérateur

1. Surcharge de l'opérateur d'adresse (fonction membre par défaut)

Regardons d'abord le code et réfléchissons à nouveau :

#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1;
	cout << &d1 << endl;
}

insérez la description de l'image ici
Les résultats sont conformes à nos attentes, et vous aurez peut-être l'impression qu'il n'y a rien à penser.
Mais nous avons dit : pour les types personnalisés, nous ne pouvons pas utiliser d'opérateurs comme les types intégrés, mais nous utilisons l'opérateur d'adresse pour l'objet d1 de la classe Date, et nous n'implémentons pas la surcharge d'opérateur, mais nous pouvons utiliser &, et ça a bien marché. pourquoi ?&&

En effet, la cinquième fonction membre par défaut : la surcharge de l'opérateur d'adresse , c'est-à-dire que si nous ne l'écrivons pas, le compilateur le générera automatiquement pour nous. Sa fonction est de nous aider à réaliser l'adresse de l'objet de type personnalisé.

Implémentation manuelle de la surcharge d'adresses

Habituellement, nous n'écrivons pas cette fonction nous-mêmes, laissons le compilateur la générer automatiquement. Et si nous implémentions cette fonction nous-mêmes ?

Le code d'implémentation est le suivant :

//取地址重载函数
#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	//取地址重载
	Date* operator&()
	{
    
    
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1;
	cout << &d1 << endl;
}

insérez la description de l'image ici

2, surcharge de l'opérateur d'adresse const (fonction membre par défaut)

Nous ne l'ajoutons généralement pas lorsque nous définissons un objet const, alors constque se passe-t-il si nous l'ajoutons à un objet ?
Alors regardons un autre morceau de code :

#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main()
{
    
    
	Date d1(2022, 1, 13);
	d1.Print();
	const Date d2(2022, 1, 13);
	d2.Print();
	return 0;
}

Nous avons constaté que la compilation a échoué. Pourquoi n'avons-nous pas réussi à appeler la fonction après
insérez la description de l'image ici
avoir ajouté l'objet ? constSelon constles raisons courantes d'ajout d'erreurs, il n'est pas difficile de penser que les autorisations doivent être élargies.

Rappelez-vous thisquel est le type de pointeur ? La réponse est : * consttapez. Ici devrait être De quel type faut- Date * const
il constprendre l'adresse de l'objet modifié ? La réponse est : const *. Ici, il devrait être que const Date*
les deux types ne correspondent pas, et constle contenu ne peut pas être modifié après la modification de l'objet, nous thisdevons donc changer le type du pointeur et en ajouter un *devant const.
insérez la description de l'image ici

Mais thisle pointeur est passé par le compilateur, on ne peut pas constl'ajouter, que faire ?
Ici, le compilateur C++ a fait un traitement spécial, il faut l'ajouter constaprès les parenthèses de la fonction pour thismodifier le pointeur.

Code correct :

#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() const
	{
    
    
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main()
{
    
    
	Date d1(2022, 1, 13);
	d1.Print();
	const Date d2(2022, 1, 13);
	d2.Print();
	return 0;
}

insérez la description de l'image ici

insérez la description de l'image ici
La "fonction membre" modifiée par const est appelée une fonction membre const . La fonction membre de classe modifiée par const modifie en fait le pointeur this implicite de la fonction membre , indiquant qu'aucun membre de la classe ne peut être modifié dans la fonction membre.

Veuillez considérer les questions suivantes :

  1. Un objet const peut-il appeler une fonction membre non const ?
    Réponse : Non, thisles autorisations seront agrandies lors du passage des pointeurs
  2. Un objet non-const peut-il appeler une fonction membre const ?
    Réponse : Oui, thisles autorisations sont restreintes lors du passage de pointeurs
  3. Les fonctions membres const peuvent-elles appeler d'autres fonctions membres non const ?
    Réponse : Non, thisles autorisations seront agrandies lors du passage des pointeurs
  4. D'autres constfonctions membres peuvent-elles être appelées dans une fonction membre non const ?
    Réponse : Oui, thisles autorisations sont restreintes lors du passage de pointeurs

Const take adresse surchargeant la mise en œuvre manuelle

De même, dans le code précédent, constlorsque nous avons pris l'adresse du type &, nous ne l'avons pas surchargé, mais nous pouvons l'utiliser, également parce que le compilateur nous a automatiquement aidés à réaliser la surcharge de l'adresse const .

Notez que les deux ne sont pas les mêmes, les deux fonctions constituent une surcharge de fonction !

surcharge de l'adresse de l'opérateur

Date* operator&()             //对非 const 对象取地址

surcharge d'adresse const

const Date* operator&()const  //对 const 对象取地址

Implémenté manuellement :

//const取地址重载函数
#include<iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year=0, int month=0, int day=0)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() const
	{
    
    
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	const Date* operator&()const //返回值const Date * 是为了与this 指针保持一致
	{
    
    
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main()
{
    
    
	const Date d1;
	cout << &d1 << endl;
	return 0;
}

insérez la description de l'image ici

insérez la description de l'image ici
Ces deux opérateurs n'ont généralement pas besoin d'être surchargés, il suffit d'utiliser la surcharge d'adresse par défaut générée par le compilateur. Seulement dans des cas particuliers, une surcharge est nécessaire, comme vouloir que contenu spécifié !

Je suppose que tu aimes

Origine blog.csdn.net/qq_65207641/article/details/129001702
conseillé
Classement