Depuis qu'il a appris le C++, Xiao Yalan a un partenaire ! ! ! (Classes et objets) (Moyen) - "C++"

Bonjour à tous du CSDN, cela fait longtemps que la colonne C++ de Xiaoyalan n'a pas été mise à jour, alors sans plus tarder, entrons dans le monde des classes et des objets ! ! !


Les 6 fonctions membres par défaut de la classe

Constructeur

destructeur

copier le constructeur


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.

N'y a-t-il vraiment rien dans la classe vide ? Non, 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.

class Date {};


Constructeur

concept

Pour la classe Date suivante :

#include<iostream>
using namespace std;
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(2023, 8, 9);
	d1.Print();
	Date d2;
	d2.Init(2023, 8, 10);
	d2.Print();
	return 0;
}

Pour la classe Date, vous pouvez définir la date de l'objet via la méthode publique Init, mais si vous appelez cette méthode pour définir les informations chaque fois que l'objet est créé, c'est un peu gênant. Pouvez-vous définir les informations lorsque l'objet est créé?

Le constructeur est une fonction membre spéciale portant le même nom que le nom de la classe, qui est automatiquement appelée 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ée qu'une seule fois dans toute la vie. cycle de l'objet.

caractéristique

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.  

En d'autres termes, le constructeur est analogue à la fonction Init ! ! !

Ses caractéristiques sont les suivantes :

  • Le nom de la fonction est le même que le nom de la classe.
  • Pas de valeur de retour (pas besoin d'écrire void).
  • Le compilateur appelle automatiquement le constructeur correspondant lorsque l'objet est instancié.
  • Le constructeur peut être surchargé (l'essentiel est d'écrire plusieurs constructeurs et de fournir plusieurs méthodes d'initialisation).
class Date
{
public:
	//无参构造函数
	Date()
	{
		cout << "Date()" << endl;
		_year = 1;
		_month = 1;
		_day = 1;
	}
	//带参构造函数
	Date(int year, int month, int day)
	{
		cout << "Date(int year, int month, int day)" << endl;
		_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.Print();
	// 调用带参的构造函数
	Date d2(2023, 8, 9);
	d2.Print();
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
	// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
	// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
	Date d3();
	d3.Print();

	return 0;
}

 

//无参构造函数
    Date()
    {         cout << "Date()" << endl;         _année = 1 ;         _mois = 1 ;         _jour = 1 ;     }     //带参构造函数     Date(int année, int mois, int jour)     {         cout << "Date(int année, int mois, int jour)" << endl;         _année = année ;         _mois = mois ;         _jour = jour ;     } 












Les deux fonctions ci-dessus peuvent en fait être combinées et écrites sous la forme de paramètres par défaut complets.

//带参构造函数
Date(int year = 1, int month = 1, int day = 1)
{
	cout << "Date(int year, int month, int day)" << endl;
	_year = year;
	_month = month;
	_day = day;
}

Cette façon d'écrire est également plus souple, vous pouvez passer un paramètre, deux paramètres, trois paramètres ou aucun paramètre.

 Considérez l'exemple suivant :

class Stack
{
public:
	Stack(size_t n = 4)
	{
		if (n == 0)
		{
			a = nullptr;
			top = capacity = 0;
		}
		else
		{
			a = (int*)malloc(sizeof(int) * n);
			if(a == nullptr)
			{
				perror("realloc fail");
				exit(-1);
			}

			top = 0;
			capacity = n;
		}
	}
	void Push(int x)
	{
		if (top == capacity)
		{
			size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
			int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);
			if (tmp == nullptr)
			{
				perror("realloc fail");
				exit(-1);
			}
			if (tmp == a)
			{
				cout << capacity << "原地扩容" << endl;
			}
			else
			{
				cout << capacity << "异地扩容" << endl;
			}

			a = tmp;
			capacity = newcapacity;
		}

		a[top++] = x;
	}

	int Top()
	{
		return a[top - 1];
	}

	void Pop()
	{
		assert(top > 0);
		--top;
	}

	void Destroy()
	{
		free(a);
		a = nullptr;
		top = capacity = 0;
	}

	bool Empty()
	{
		return top == 0;
	}
private:
	// 成员变量
	int* a;
	int top;
	int capacity;
};

int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);
	
	while (!st1.Empty())
	{
		cout << st1.Top() << " ";
		st1.Pop();
	}
	cout << endl;

	st1.Destroy();

	//Stack st2(1000);
	Stack st2;
	for (size_t i = 0; i < 1000; i++)
	{
		st2.Push(i);
	}

	while (!st2.Empty())
	{
		cout << st2.Top() << " ";
		st2.Pop();
	}
	cout << endl;

	st2.Destroy();
	return 0;
}

 

 

Le constructeur est une fonction membre par défaut, s'il n'est pas écrit, le compilateur le générera automatiquement.

Les caractéristiques du constructeur par défaut généré par compilation :

  • Si nous n'écrivons pas, il sera généré, et si nous écrivons, il ne sera pas généré.
  • Les membres des types intégrés ne sont pas gérés (C++11, déclare la prise en charge des valeurs par défaut).
  • Seuls les membres de types personnalisés seront traités et le constructeur par défaut de ce membre sera appelé.

Si aucun constructeur n'est 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.

class Date
{
public:
	如果用户显式定义了构造函数,编译器将不再生成
	//Date(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类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
	// 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
	// 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
	Date d1;
	d1.Print();
	return 0;
}

 

Par conséquent, le constructeur généré par défaut est généralement sans valeur, mais il est très utile dans certains scénarios. Xiao Yalan a déjà écrit un sujet, qui consiste à implémenter une file d'attente avec deux piles. Vous pouvez écrire un constructeur ou non.

 

// 两个栈实现一个队列
class MyQueue
{
private:
	Stack _pushst;
	Stack _popst;
};

Résumé : En général, nous devons écrire notre propre constructeur et décider de la méthode d'initialisation

Les variables membres sont toutes des types personnalisés, vous pouvez envisager de ne pas écrire de constructeurs

Beaucoup de gens ont des doutes sur les fonctions membres par défaut générées 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 d appelle le constructeur par défaut généré par le compilateur, mais l'objet d _année/_mois/_jour 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..., le type personnalisé est le type que nous définissons nous-mêmes en utilisant class/struct/union, etc. Si vous regardez le programme suivant, vous constaterez que le compilateur génère la structure par défaut La fonction appellera son constructeur par défaut pour le membre de type personnalisé _t.

int* et Date* (pointeurs) sont des types intégrés ! ! !

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

 

Remarque : dans C++11, un correctif a été corrigé pour le défaut de non-initialisation des membres de type intégré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.

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

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. 

La construction qui peut être appelée sans passer de paramètres est la construction 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;
}
int main()
{
	Test();
	return 0;
}

Le code ci-dessus ne peut pas être compilé.

Si le constructeur sans argument est masqué ou si le constructeur par défaut est masqué, la compilation peut être passée ! ! ! !


destructeur

concept

Grâce à l'étude du constructeur précédent, nous savons comment un objet est apparu et comment cet objet a-t-il disparu ?

Destructeur : contrairement à la fonction du constructeur, le destructeur ne complète pas la destruction de l'objet lui-même, et la destruction de l'objet local est effectuée par le compilateur. Lorsque l'objet est détruit, il appellera automatiquement le destructeur pour terminer le nettoyage des ressources dans l'objet.

En d'autres termes, le destructeur est analogue à la fonction Destroy ! ! !

caractéristique

Un destructeur est une fonction membre spéciale dont les caractéristiques sont les suivantes :

  • Le nom du destructeur est précédé du caractère ~ avant le nom de la classe.
  • Aucun paramètre et aucun type de retour.
  • 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. Remarque : Les destructeurs ne peuvent pas être surchargés.
  • Lorsque le cycle de vie de l'objet se termine, le système de compilation C++ appelle automatiquement le destructeur.

Le destructeur par défaut est similaire au constructeur par défaut : les membres de type intégré ne seront pas traités et les membres de type personnalisés appelleront le destructeur de ce membre.

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		cout << "Date(int year = 1, int month = 1, int day = 1)" << endl;

		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	~Date()
	{
		cout << "~Date()" << endl;
	}

private:
	int _year = 1;   // 声明给的缺省值
	int _month = 1;
	int _day = 1;
};

class Stack
{
public:
	Stack(size_t n = 4)
	{
		cout << "Stack(size_t n = 4)" << endl;

		if (n == 0)
		{
			a = nullptr;
			top = capacity = 0;
		}
		else
		{
			a = (int*)malloc(sizeof(int) * n);
			if (a == nullptr)
			{
				perror("realloc fail");
				exit(-1);
			}

			top = 0;
			capacity = n;
		}
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(a);
		a = nullptr;
		top = capacity = 0;
	}

	void Push(int x)
	{
		if (top == capacity)
		{
			size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
			int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);
			if (tmp == nullptr)
			{
				perror("realloc fail");
				exit(-1);
			}
			if (tmp == a)
			{
				cout << capacity << "原地扩容" << endl;
			}
			else
			{
				cout << capacity << "异地扩容" << endl;
			}

			a = tmp;
			capacity = newcapacity;
		}

		a[top++] = x;
	}

	int Top()
	{
		return a[top - 1];
	}

	void Pop()
	{
		assert(top > 0);
		--top;
	}

	void Destroy()
	{
		free(a);
		a = nullptr;
		top = capacity = 0;
	}

	bool Empty()
	{
		return top == 0;
	}
private:
	// 成员变量
	int* a;
	int top;
	int capacity;
};

// 两个栈实现一个队列
class MyQueue
{
private:
	Stack _pushst;
	Stack _popst;
};
int main()
{
	Date d1;
	Date d2;

	Stack st1;
	Stack st2;
	return 0;
}

 En fait, la classe date n'a pas besoin d'écrire de destructeur ! ! !

 Pour une structure de données comme une pile, vous devez écrire un destructeur ! ! !

 

Est-ce que quelque chose est fait en ce qui concerne les destructeurs générés automatiquement par le compilateur ? Dans le programme suivant, nous verrons que le destructeur par défaut généré par le compilateur appelle son destructeur pour le membre de type personnalisé.

S'il n'y a pas de ressource d'application dans la classe, le destructeur ne peut pas être écrit et le destructeur par défaut généré par le compilateur peut être utilisé directement, tel que

Classe Date ; lorsqu'il existe une application de ressources, elle doit être écrite, sinon cela entraînera une fuite de ressources, comme la classe Stack.

class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;

	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

// 程序运行结束后输出:~Time()
// 在main函数中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
// 因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是
// 内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在
// d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是:main函数
// 中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函
// 数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time
// 类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁
// main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
// 注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数

 


copier le constructeur

concept

Dans la vraie vie, il peut exister un moi comme vous, que nous appelons un jumeau.

Lors de la création d'un objet, pouvez-vous créer un nouvel objet qui soit exactement le même qu'un objet existant ?  

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 une décoration const), qui est automatiquement appelé par le compilateur lors de la création d'un nouvel objet avec un objet de type classe existant.

fonctionnalité

Le constructeur de copie est également une fonction membre spéciale avec les caractéristiques suivantes :

  • Le constructeur de copie est une forme surchargée du constructeur.
  • Le paramètre du constructeur de copie est un seul et doit être une référence à un objet de type classe (objet de même type). Si la méthode de passage par valeur est utilisée, le compilateur signalera directement une erreur, car cela provoquera une infinité appels récursifs .

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// Date(const Date& d)// 正确写法
	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;
}

 

 

 

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(Date& d)
	{
		cout << "Date(Date& d)" << endl;

		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	
private:
	// 内置类型
	int _year;
	int _month;
	int _day;
};

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}

		_capacity = capacity;
		_size = 0;
	}

	Stack(Stack& s)
	{
		cout << "Stack(Stack& s)" << endl;
		// 深拷贝
		_array = (DataType*)malloc(sizeof(DataType) * s._capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}

		memcpy(_array, s._array, sizeof(DataType) * s._size);
		_size = s._size;
		_capacity = s._capacity;
	}

	void Push(DataType data)
	{
		_array[_size] = data;
		_size++;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}
private:
	// 内置类型
	DataType* _array;
	int _capacity;
	int _size;
};

void func1(Date d)
{
	d.Print();
}
// 期望呢,s要插入一些数据,s的改变,不影响s1
void func2(Stack s)
{
	s.Push(1);
	s.Push(2);
}

int main()
{
	Date d1(2023, 7, 21);
	func1(d1);

	Stack s1;
	func2(s1);

	Stack s2(s1);

	// 以下两个写法是等价的,都是拷贝构造
	Date d2(d1);
	Date d3 = d1;


	return 0;
}

 

 

 

S'il n'est pas explicitement défini, le compilateur générera 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.

class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const Time&)" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;

	// 自定义类型
	Time _t;
};

int main()
{
	Date d1;
	// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
	// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数
	Date d2(d1);
	return 0;
}

 Remarque : Dans le constructeur de copie par défaut généré par le compilateur, le type intégré est directement copié en mode octet, tandis que le type personnalisé est copié en appelant son constructeur de copie.

Nous n'écrivons pas, ne compilons pas la construction de copie générée par défaut, qui est différente des fonctionnalités du constructeur précédent

  • type intégré, copie de valeur
  • Un type personnalisé, appelez sa copie 

Résumé : La date ne nous oblige pas à implémenter la construction de copie, elle peut être utilisée par la génération par défaut

        Stack a besoin que nous implémentions nous-mêmes la structure de copie de la copie profonde, et la génération par défaut causera des problèmes

MyQueue est très utile pour plusieurs fonctions générées par défaut, un gagnant dans la vie

class MyQueue
{ privé :     Stack _pushst ;     pile _popst ; } ;



MaQueue mq1 ;
MaQueue mq2 = mq1 ;

 

Le constructeur de copie par défaut généré par le compilateur peut déjà copier des valeurs ordonnées par octets. Dois-je l'implémenter explicitement moi-même ? Bien sûr, des classes comme la classe Date ne sont pas nécessaires. Qu'en est-il des cours suivants ? Essayez de le vérifier?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后学的深拷贝去解决。
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(s1);
	return 0;
}

 

Remarque : 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.  

Scénarios d'appel typiques du constructeur de copie :

  1. Créer un nouvel objet à partir d'un objet existant
  2. Le type de paramètre de fonction est un objet de type classe
  3. Le type de valeur de retour de la fonction est un objet de type classe  
class Date
{
public:
	Date(int year, int minute, int day)
	{
		cout << "Date(int,int,int):" << this << endl;
	}
	Date(const Date& d)
	{
		cout << "Date(const Date& d):" << this << endl;
	}
	~Date()
	{
		cout << "~Date():" << this << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
Date Test(Date d)
{
	Date temp(d);
	return temp;
}
int main()
{
	Date d1(2022, 1, 13);
	Test(d1);
	return 0;
}

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.  


Tous les codes sources sont les suivants :

#include<iostream>
#include<assert.h>
en utilisant l'espace de noms std ;
class Date
{ public:     void Init(int année, int mois, int jour)     {         _year = year;         _mois = mois ;         _jour = jour ;     }     void Print()     {         cout << _year << "-" << _month << "-" << _day << endl;     }










privé :
    entier _année ;
    entier _mois ;
    entier _jour ;
} ;

int main()
{     Date d1;     d1.Init(2023, 8, 9);     d1.Print();     Date j2 ;     d2.Init(2023, 8, 10);     d2.Print();     renvoie 0 ; }








 

class Date
{ public:     无参构造函数     //Date()     //{     // cout << "Date()" << endl;     // _année = 1 ;     // _mois = 1 ;     // _jour = 1 ;     //}     带参构造函数     //Date(int année, int mois, int jour)     //{     // cout << "Date(int année, int mois, int jour)" << endl;     // _année = année ;     // _mois = mois ;     // _jour = jour ;     //}     //带参构造函数     Date(int année = 1, int mois = 1, int jour = 1)     {         cout << "Date(int année, int mois,






















        _mois = mois ;
        _jour = jour ;
    }
    void Print()
    {         cout << _année << "-" << _mois << "-" << _jour << endl ;     } privé :     int _année ;     int _mois ;     int _jour ; } ; int main() {     // Appel du constructeur sans argument     Date d1 ;     d1.Print();     // Appel du constructeur d'argument-argument     Date d2(2023, 8, 9);     d2.Print();     // Remarque : Si l'objet est créé via un constructeur sans argument, l'objet n'a pas besoin d'être suivi de parenthèses, sinon il devient une déclaration de fonction //     La fonction du code suivant : la fonction d3 est déclarée, la fonction n'a pas paramètres, et retourne un objet de type date     // avertissement C4930 : "Date d3(void)": Fonction prototype non appelée (a-t-elle été définie intentionnellement avec une variable ?)     //Date d3();     //d3.Print();




















    Date j3(2023);
    d3.Print();
    Date j4(2023, 8);
    d4.Print();
    renvoie 0 ;
}

class Stack
{ public :     Stack(size_t n = 4)     {         if (n == 0)         {             a = nullptr ;             haut = capacité = 0 ;         }         sinon         {             a = (int*)malloc(sizeof(int) * n);             if(a == nullptr)             {                 perror("realloc fail");                 sortie(-1);             }















            haut = 0 ;
            capacité = n ;
        }
    }
    void Push(int x)
    {         if (top == capacity)         {             size_t newcapacity = capacity == 0 ? 4 : capacité * 2 ;             int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);             if (tmp == nullptr)             {                 perror("realloc fail");                 sortie(-1);             }             if (tmp == a)             {                 cout << capacité << "原地扩容" << endl;             }             sinon             {















                cout << capacité << "Extension à distance" << endl;
            }

            a = tmp ;
            capacité = nouvelle capacité ;
        }

        a[top++] = x ;
    }

    int Top()
    {         renvoie un[top - 1] ;     }

    void Pop()
    {         assert(top > 0);         --haut;     }


    void Destroy()
    {         free(a);         a = nullptr ;         haut = capacité = 0 ;     }



    bool Empty()
    {         return top == 0 ;     } private :     // variable membre     int* a ;     int top ;     int capacity ; } ;







int main()
{     Pile st1 ;     st1.Push(1);     st1.Appuyer(2);     st1.Appuyer(3);     st1.Appuyer(4);     while (!st1.Empty())     {         cout << st1.Top() << " " ;         st1.Pop();     }     cout << endl;





    





    st1.Destroy();

    Pile st2(1000);
    // Empiler st2 ;
    for (size_t je = 0; je < 1000; je++)
    {         st2.Push(i);     }

    while (!st2.Empty())
    {         cout << st2.Top() << " " ;         st2.Pop();     }     cout << endl;



    st2.Destroy();
    renvoie 0 ;
}

class Date
{ public :     si l'utilisateur définit explicitement le constructeur, le compilateur ne générera plus     //Date(int année, int mois, int jour)     //{     // _année = année ;     // _mois = mois ;     // _jour = jour ;     //}     void Print()     {         cout << _année << "-" << _mois << "-" << _jour << endl;     }








    



privé :
    entier _année ;
    entier _mois ;
    entier _jour ;
} ;

int main()
{     // Après avoir masqué le constructeur dans la classe Date, le code peut être compilé, car le compilateur génère un constructeur par défaut sans paramètres     // Libérez le constructeur dans la classe Date, et le code ne se compile pas, car une fois Définir explicitement un constructeur, le compilateur ne générera plus     // de constructeur sans argument et signalera une erreur après l'avoir relâché : erreur C2512 : "Date" : aucun constructeur par défaut approprié disponible     Date d1 ;     d1.Print();     return 0 ; }






// Le constructeur est aussi la fonction membre par défaut. Si nous ne l'écrivons pas, le compilateur le générera automatiquement.
// Les caractéristiques de la structure par défaut générée par la compilation :
// 1. Elle sera générée si nous ne le faisons pas Si nous écrivons un constructeur, il ne sera pas généré
// 2. Les membres des types intégrés ne seront pas traités (C++11, la déclaration prend en charge les valeurs par défaut)
// 3. Les membres des types personnalisés seront traité, et le constructeur par défaut de ce membre sera rappelé

// Résumé : En général, nous devons écrire notre propre constructeur et décider de la méthode d'initialisation
// Toutes les variables membres sont des types personnalisés, nous pouvons donc envisager de ne pas écrire de constructeur

 

class Time
{ public:     Time()     {         cout << "Time()" << endl;         _heure = 0 ;         _minute = 0 ;         _seconde = 0 ;     }







privé :
    entier _heure ;
    entier _minute ;
    entier _seconde ;
} ;

class Date
{ private :     // type de base (type intégré)     int _year ;     int _month ;     int _day ;     // type personnalisé     Time _t ; } ;







int main()
{     Date j;     renvoie 0 ; }


 

class Time
{ public:     Time()     {         cout << "Time()" << endl;         _heure = 0 ;         _minute = 0 ;         _seconde = 0 ;     } privé :     entier _heure ;     entier _minute ;     entier _seconde ; } ;












class Date
{ private :     // type de base (type intégré)     int _year = 1970 ;     int _month = 1 ;     int _day = 1 ;     // type personnalisé     Time _t ; } ;







int main()
{     Date j;     renvoie 0 ; }


 

classe Date
{ public :     Date()     {         _année = 1900 ;         _mois = 1 ;         _jour = 1 ;     }     Date(int année = 1900, int mois = 1, int jour = 1)     {         _year = année ;         _mois = mois ;         _jour = jour ;     }












privé :
    entier _année ;
    entier _mois ;
    entier _jour ;
} ;

// La fonction de test suivante compile-t-elle ?
void Test()
{     Date d1; } int main() {     Test();     return 0; }






 

class Date
{ public:     Date(int année = 1, int mois = 1, int jour = 1)     {         cout << "Date(int année = 1, int mois = 1, int jour = 1)" << endl;



        _année = année ;
        _mois = mois ;
        _jour = jour ;
    }
    void Print()
    {         cout << _year << "/" << _month << "/" << _day << endl;     }

    ~Date()
    {         cout << "~Date()" << endl;     }

privé :
    int _year = 1 ; // valeur par défaut déclarée
    int _month = 1 ;
    int _day = 1 ;
} ;

class Stack
{ public:     Stack(size_t n = 4)     {         cout << "Stack(size_t n = 4)" << endl;



        si (n == 0)
        {             a = nullptr ;             haut = capacité = 0 ;         }         sinon         {             a = (int*)malloc(sizeof(int) * n);             if (a == nullptr)             {                 perror("realloc fail");                 sortie(-1);             }










            haut = 0 ;
            capacité = n ;
        }
    }

    ~Stack()
    {         cout << "~Stack()" << endl;         libre(a);         a = nullptr ;         haut = capacité = 0 ;     }




    void Push(int x)
    {         if (top == capacity)         {             size_t newcapacity = capacity == 0 ? 4 : capacité * 2 ;             int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);             if (tmp == nullptr)             {                 perror("realloc fail");                 sortie(-1);             }             if (tmp == a)             {                 cout << capacité << "原地扩容" << endl;             }             else             {                 cout << capacité << "异地扩容" << endl;             }
















            a = tmp ;
            capacité = nouvelle capacité ;
        }

        a[top++] = x ;
    }

    int Top()
    {         renvoie un[top - 1] ;     }

    void Pop()
    {         assert(top > 0);         --haut;     }


    void Destroy()
    {         free(a);         a = nullptr ;         haut = capacité = 0 ;     }



    bool Empty()
    {         return top == 0 ;     } private :     // variable membre     int* a ;     int top ;     int capacity ; } ;







// Deux piles implémentent une
classe de file d'attente MyQueue
{ private:     Stack _pushst;     Stack _popst; }; int main() {     Date d1;     Date d2;







    Empilez st1 ;
    Empilez st2 ;
    renvoie 0 ;
}

 

 

class Time
{ public:     ~Time()     {         cout << "~Time()" << endl;     } privé :     entier _heure ;     entier _minute ;     entier _seconde ; } ;









class Date
{ private :     // type de base (type intégré)     int _year = 1970 ;     int _month = 1 ;     int _day = 1 ;




    // type personnalisé
    Heure _t ;
} ;

int main()
{     Date j;     renvoie 0 ; }


// Après l'exécution du programme, la sortie : ~Time()
// Il n'y a pas d'objet de la classe Time directement dans la fonction principale, pourquoi le destructeur de la classe Time est-il appelé à la fin ?
// Parce que : l'objet Date d est créé dans la méthode principale, et d contient 4 variables membres, parmi lesquelles _year, _month, _day sont // des membres de type intégrés, aucun nettoyage de ressource n'est requis lors de la destruction, et enfin le système les enregistre directement
La mémoire peut être récupérée ; et _t est un objet de la classe Time, donc quand
// d est détruit, l'objet _t de la classe Time qu'il contient doit être détruit, donc le destructeur de la classe Time doit être appelé. Mais :
le destructeur de la classe Time ne peut pas être directement appelé dans la fonction principale //, l'objet réel à libérer est l'objet de la classe Date, donc le compilateur appellera le destructeur de la classe Date //
nombre, et si Date est pas explicitement fourni, il sera compilé Le compilateur va générer un destructeur par défaut pour la classe Date, le but est d'appeler le
destructeur de la classe Time // à l'intérieur, c'est-à-dire que lorsque l'objet Date est détruit, il faut assurez-vous que chaque objet personnalisé qu'il contient peut être détruit correctement
// La fonction main n'appelle pas directement le destructeur de la classe Time, mais appelle explicitement le destructeur par défaut généré par le compilateur pour la classe Date // Note
: l'objet de quelle classe est créé appelle le destructeur de cette classe pour détruire cette classe L'objet de la classe appelle le destructeur de la classe
 

class Date
{ public :     Date (int année = 1, int mois = 1, int jour = 1)     {         _year = année ;         _mois = mois ;         _jour = jour ;     }






    Date(Date& d)
    {         cout << "Date(Date& d)" << endl;

        _année = j._année ;
        _mois = j._mois ;
        _jour = j._jour ;
    }

    void Print()
    {         cout << _year << "/" << _month << "/" << _day << endl;     } private :     // 内置类型     int _year ;     entier _mois ;     entier _jour ; } ;


    





typedef int Type de données ;
class Stack
{ public:     Stack(size_t capacity = 3)     {         _array = (DataType*)malloc(sizeof(DataType) * capacity);         if (NULL == _array)         {             perror("malloc申请空间失败!!!");             retour;         }








        _capacité = capacité ;
        _taille = 0 ;
    }

    Stack(Stack& s)
    {         cout << "Stack(Stack& s)" << endl;         // copie profonde         _array = (DataType*)malloc(sizeof(DataType) * s._capacity);         if (NULL == _array)         {             perror("l'espace d'application malloc a échoué !!!");             retour;         }







        memcpy(_array, s._array, sizeof(DataType) * s._size);
        _size = s._size ;
        _capacité = s._capacité ;
    }

    void Push(DataType data)
    {         _array[_size] = data;         _taille++ ;     }


    ~Stack()
    {         cout << "~Stack()" << endl;         libre(_array);         _array = nullptr ;         _taille = _capacité = 0 ;     } private :     // 内置类型     DataType* _array ;     entier _capacité ;     entier _taille ; } ;










void func1(Date d)
{     d.Print(); } // On s'attend à ce que s insère des données, et le changement de s n'affectera pas s1 void func2(Stack s) {     s.Push(1);     s .Poussez( 2); }







int main()
{     Date j1(2023, 7, 21);     fonction1(d1);

    pile s1 ;
    fonction2(s1);

    Pile s2(s1);

    // Les deux méthodes d'écriture suivantes sont équivalentes, les deux sont des constructions par copie
    Date d2(d1);
    Date d3 = d1;


    renvoie 0 ;
}

class Date
{ public:     Date(int year = 1900, int month = 1, int day = 1)     {         _year = year;         _month = month;         _day = day;     }     // Date(const Date& d)// Manière correcte d'écrire     Date( const Date& d)// Mauvaise façon d'écrire : erreur de compilation, causera une récursivité infinie     {         _year = d._year;         _month = d._month;         _day = d._day;     } private:     int _year;     int _month;     int _day ; };


















int main()
{     Date d1;     date d2(d1);     renvoie 0 ; }




 

class Time
{ public :     Time()     {         _hour = 1 ;         _minute = 1 ;         _seconde = 1 ;     }     Heure(temps const& t)     {         _hour = t._hour ;         _minute = t._minute ;         _seconde = t._seconde ;         cout << "Time::Time(const Time&)" << endl;     } privé :     entier _heure ;     entier _minute ;     entier _seconde ; } ;


















class Date
{ private :     // type de base (type intégré)     int _year = 1970 ;     int _month = 1 ;     int _day = 1 ;




    // type personnalisé
    Heure _t ;
} ;

int main()
{     Date d1;     // Utilisez le d1 existant pour copier et construire d2, ici le constructeur de copie de la classe Date sera appelé     // Mais la classe Date ne définit pas explicitement un constructeur de copie, le compilateur donnera le Classe Date Génère un constructeur de copie par défaut     Date d2(d1);     retourne 0; }





// Vous constaterez ici que le programme suivant va planter ? Ici, nous avons besoin de la copie profonde que nous apprendrons plus tard à résoudre.
typedef int DataType;
class Stack
{ public:     Stack(size_t capacity = 10)     {         _array = (DataType*)malloc(capacity * sizeof(DataType));         if (nullptr == _array)         {             perror("malloc n'a pas pu demander d'espace ") ;             retour ;         }         _size = 0 ;         _capacity = capacité ;     }     void Push(const DataType& data)     {         // CheckCapacity();         _array[_size] = data;         _size++;     }     ~Stack()     {         if (_array)





















        {             libre(_array);             _array = nullptr ;             _capacité = 0 ;             _taille = 0 ;         }     }





privé :
    Type de données* _array ;
    size_t _size ;
    taille_t _capacité ;
} ;

int main()
{     Pile s1 ;     s1.Appuyer(1);     s1.Appuyer(2);     s1.Appuyer(3);     s1.Appuyer(4);     Pile s2(s1);     renvoie 0 ; }







class Date
{ public:     Date(int année, int minute, int jour)     {         cout << "Date(int,int,int):" << this << endl;     }     Date(const Date& d)     {         cout << "Date(const Date& d):" << this << endl;     }     ~Date()     {         cout << "~Date():" << this << endl;     } privé :     entier _année ;     entier _mois ;     entier _jour ; } ; Date Test(Date d) {     Date temp(d);     température de retour ; } int main() {     Date d1(2022, 1, 13);     Essai(d1);



























    renvoie 0 ;
}

 


 D'accord, c'est la fin du contenu d'apprentissage de Xiao Yalan pour aujourd'hui, continuons à travailler dur ! ! ! !

 

Je suppose que tu aimes

Origine blog.csdn.net/weixin_74957752/article/details/132197135
conseillé
Classement