Une des trois caractéristiques majeures de l'orienté objet - l'héritage (Partie 1)

Table des matières

Préambule

1. Qu'est-ce que le Cheng Cheng ?

1.1 Définition de l'héritage

1.2 Formalités héritées

1.2.1 Formes d'utilisation héritées

1.2.2 Héritage et qualificatifs d'accès

Deuxièmement, conversion de copie d'objet de classe de base et de classe dérivée

3. Portée de l'héritage

Quatrièmement, la fonction membre par défaut dans la classe/sous-classe dérivée

Sixième, Cheng Cheng et You Yuan

 Six, héritage et membres statiques

Résumer


Préambule

Cet article explique principalement en détail l'héritage de l'une des trois caractéristiques majeures de l'orienté objet.

Ici, nous expliquerons principalement le sens et les diverses propriétés de 継成, et amènerons tout le monde à comprendre 継成

1. Qu'est-ce que le Cheng Cheng ?

1.1 Définition de l'héritage

Prenons un petit exemple

En observant l'image ci-dessus, nous avons constaté que la vache de Picasso faisait abstraction des caractéristiques de la vache, de sorte que le buffle, la vache et le bétail ci-dessous incluent essentiellement toutes les caractéristiques de la vache dans l'image ci-dessus, ce qui peut être considéré comme un héritage. La vache de Picasso est Elle peut être considérée comme une catégorie mère, et le buffle, la vache laitière et le scalpeur peuvent être considérés comme une sous-catégorie.

Le mécanisme d'héritage (héritage) est la méthode la plus importante de programmation orientée objet pour rendre le code réutilisable . Il permet aux programmeurs d'étendre et d'ajouter des fonctions tout en conservant les caractéristiques de la classe d'origine, générant ainsi de nouvelles classes, appelées classes dérivées . L'héritage présente la structure hiérarchique de la programmation orientée objet, reflétant le processus cognitif du simple au complexe. La réutilisation à laquelle nous avons été exposés auparavant est la réutilisation des fonctions, et l'héritage est la réutilisation des niveaux de conception de classe .

Ici, nous pouvons simplement écrire une classe dérivée en premier, et tout le monde peut en faire l'expérience

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;

class person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
private:
	string _name = "张三";
	int _age = 18;
};

class student :public person
{

private:
	int _stuNumber;
};


int main()
{
	student aa;
	aa.print();
	return 0;
}

En exécutant comme ci-dessus, nous pouvons constater qu'après l'héritage, les membres de la classe parent feront partie de la sous-classe, et nous pouvons également appeler les fonctions membres de la classe parent via la sous-classe, alors quelles fonctionnalités a-t-il ? Allons-y étape par étape, puis expliquons le format de l'héritage.

1.2 Formalités héritées

1.2.1 Formes d'utilisation héritées

 Comme le montre la figure ci-dessus, la personne est appelée classe de base/parente et l'étudiant est appelé sous-classe/classe dérivée.

1.2.2 Héritage et qualificatifs d'accès

 Le mode d'héritage correspond au qualificateur d'accès par paires, et les modifications spécifiques sont les suivantes.

 Résumer:

1. Les membres privés de la classe de base sont invisibles, quelle que soit la manière dont ils sont hérités dans la classe dérivée . L'invisibilité signifie ici que les membres privés de la classe de base sont toujours hérités dans l'objet de la classe dérivée, mais l'objet de la classe dérivée est grammaticalement limité pour pouvoir y accéder, que ce soit à l'intérieur ou à l'extérieur de la classe .
2. Les membres privés de la classe de base ne sont pas accessibles dans la classe dérivée. Si le membre de la classe de base ne souhaite pas être accessible directement en dehors de la classe, mais doit être accessible dans la classe dérivée, il est défini comme protégé. On peut voir que le qualificateur de membre protégé est dû à l'héritage .
3. En fait, si nous résumons le tableau ci-dessus, nous constaterons que les membres privés de la classe de base ne sont pas visibles dans la sous-classe. public > protected > private peut être considéré comme des permissions, et les permissions peuvent seulement être réduites mais pas agrandies .        
4. Lorsque vous utilisez le mot-clé class, la méthode d'héritage par défaut est private et lorsque vous utilisez struct, la méthode d'héritage par défaut est public, mais il est préférable d'écrire explicitement la méthode d'héritage .
5. En pratique, l'héritage public est généralement utilisé, et l'héritage protégé/privé est rarement utilisé , et l'utilisation de l'héritage protégé/privé n'est pas préconisée, car les membres hérités protégés/privés ne peuvent être utilisés que dans des classes dérivées. la maintenabilité étendue n'est pas forte.

class person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "张三";
private:
	int _age = 18;
};

class student :private person
{
private:
	int _stuNumber;
};


int main()
{
	student aa;
	//私有継承的话父类我们办法直接访问但是可以间接访问
	aa.print();
	return 0;
}

 Comme le montre la figure, s'il s'agit d'un héritage privé, toutes les fonctions et variables membres de la classe parent ne sont pas accessibles directement, nous n'avons donc vraiment aucun moyen d'y accéder. Nous pouvons accéder aux membres de la classe parent via les fonctions membres de la sous-classe .

 En fait, cela ressemble à une famille : des moyens publics pour ouvrir la porte aux étrangers, des moyens protégés pour empêcher les étrangers et des moyens privés pour empêcher les étrangers et votre propre famille.

Deuxièmement, conversion de copie d'objet de classe de base et de classe dérivée

  1. Les objets des classes dérivées peuvent être affectés aux objets des classes de base/pointeurs des classes de base/références des classes de base . Il y a ici un terme figuratif appelé tranchage
    ou découpage. Cela signifie couper la partie de la classe mère dans la classe dérivée et l'affecter au passé
  2. Les objets de la classe de base ne peuvent pas être affectés aux objets de la classe dérivée.

  3. Le pointeur ou la référence de la classe de base peut être affecté au pointeur ou à la référence de la classe dérivée via cast.
    Mais il doit être sûr lorsque le pointeur de la classe de base pointe vers l'objet de la classe dérivée. Ici, si la classe de base est un type polymorphe, le dynamic_cast de RTTI (RunTime Type Information) peut être utilisé pour identifier et effectuer une conversion sécurisée. (ps: nous expliquerons cela plus tard, apprenons d'abord à le connaître)

    class person
    {
    public:
    	void print()
    	{
    		cout << "name:" << _name << endl;
    		cout << "age:" << _age << endl;
    	}
    protected:
    	string _name = "张三";
    	int _age = 18;
    };
    
    class student :public person
    {
    private:
    	int _stuNumber;
    };
    
    
    int main()
    {
    	student aa;
    	//1.子类对象可以赋值给基类的对象,指针,引用
    	person p = aa;
    	person* ptr = &aa;
    	person& pp = aa;
    
    	//2.基类对象不能赋值给派生类
    	//aa = p;会报错
    
    	//3.基类的指针可以通过强转赋值给子类的指针
    	ptr = &aa;
    	student* ps1 = (student*)ptr;//这种情况可以
    	//这个ptr指向的空间和aa指向的空间是相同的,所以不会越界
    
    	ptr = &p;
    	student* ps2 = (student*)ptr;//这种情况可能会存在越界访问
    	//因为ptr只有父类的内容没有子类的内容
    	return 0;
    }

     

    Alors, comment se fait la conversion d'affectation entre la classe de base et la classe dérivée, ce que nous expliquerons plus tard.

3. Portée de l'héritage

1. Dans le système d'héritage, la classe de base et la classe dérivée ont des portées indépendantes .

class person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "张三";
	int _age = 18;
};

class student :public person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
		cout << "stunumber:" << _stuNumber << endl;

	}
private:
	int _stuNumber=1111111;
};


int main()
{
	student aa;
	aa.print();
	return 0;
}

Que se passera-t-il lorsque le code ci-dessus sera exécuté ?

 Comme le montre la figure ci-dessus, nous avons constaté que lorsque nous appelons print, bien que la sous-classe et la classe parente aient print, quelle est la dernière chose que nous appelons print dans la sous-classe ? Quelle est la situation ? C'est en fait ce qu'on appelle cacher .

2. Il existe des membres portant le même nom dans la sous-classe et la classe parent, et les membres de la sous-classe bloqueront l'accès direct de la classe parent aux membres portant le même nom. Cette situation est appelée masquage ou redéfinition . Dans ce cas, les membres de la sous-classe portant le même nom seront accédés en premier. (Dans les fonctions membres de sous-classe, base class::base class members peut être utilisé pour afficher access )
3. Il convient de noter que si la fonction membre est masquée, seul le même nom de fonction est requis pour constituer le masquage .
4. Notez qu'en pratique, il est préférable de ne pas définir de membres portant le même nom dans le système d'héritage .

 Comme le montre la figure, via le qualificateur d'accès, nous pouvons également accéder aux membres du même nom de la classe parent, mais en héritage réel, il est préférable de ne pas définir la fonction membre du même nom, car elle est de peu importance.

Donnons aux ironies une question à choix multiples


class A
{
public:
 void fun()
 {
 cout << "func()" << endl;
 }
};
class B : public A
{
public:
 void fun(int i)
 {
 A::fun();
 cout << "func(int i)->" <<i<<endl;
 }
};
void Test()
{
 B b;
 b.fun(10);
};

对于上面程序的运行,下面选项那个正确?
A:A中fun和B中fun构成函数重载
B:A中fun和B中fun构成隐藏
C:程序错误
D:上面三个选项均不正确

Pour cette question, nous choisissons B. Premièrement, fun en A et fun en B ne sont pas dans le même domaine, ils ne constituent donc pas une surcharge de fonctions. Le fun en A a le même nom que le fun en B, donc ils sont cachés.

Quatrièmement, la fonction membre par défaut dans la classe/sous-classe dérivée

6 fonctions membres par défaut, "default" signifie que si nous ne l'écrivons pas, le compilateur en générera automatiquement une pour nous, alors comment
ces fonctions membres sont-elles générées dans la classe dérivée ?

1. Lorsque le constructeur de la classe dérivée est appelé, cette partie des membres de la classe de base doit appeler le constructeur par défaut de la classe de base pour la construction . Si la classe de base n'a pas de constructeur par défaut , alors la classe dérivée doit appeler explicitement à la séquence d'initialisation du constructeur .

2. Le constructeur de copie de la classe dérivée doit appeler le constructeur de copie de la classe de base pour terminer la construction de copie de la classe de base

3. L'opérateur= de la classe dérivée doit appeler l'opérateur= de la classe de base pour terminer l'affectation de la classe de base .

4. Lorsque le destructeur de la classe dérivée est appelé, le destructeur de la classe de base est appelé après que le destructeur de la classe dérivée est appelé , car cela peut garantir que les membres de la classe dérivée sont nettoyés en premier, puis les membres de la classe de base sont nettoyées pour éviter d'abord nettoyer les membres de la classe de base, puis la situation de pointeurs sauvages apparaît.

5. Structure d'initialisation de la classe dérivée, appelez d'abord le constructeur de la classe de base pour initialiser la partie de la classe de base, puis initialisez la classe dérivée.

6. L'objet de la classe dérivée est détruit, la classe dérivée est détruite en premier, puis la classe de base est détruite.

7. Étant donné que le destructeur dans certains scénarios ultérieurs doit être réécrit, l'une des conditions de réécriture est que le nom de la fonction soit le même (nous l'expliquerons plus tard). Ensuite, le compilateur effectuera un traitement spécial sur le nom du destructeur et le traitera comme destructor(), donc lorsque le destructeur de classe parent n'ajoute pas de virtual, le destructeur de sous-classe et le destructeur de classe parent forment une relation cachée.

 En général, pour les 6 fonctions membres par défaut, la classe dérivée et la classe de base appellent leurs fonctions membres par défaut respectives .

class Person
{
public:
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student(const char* name, int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}

	Student(const Student& s)
		: Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}

	Student& operator = (const Student& s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};
void Test()
{
	Student s1("jack", 18);
	Student s2(s1);
	Student s3("rose", 17);
	s1 = s3;
}
int main()
{
	Test();
	return 0;
}

 En voyant cela, on devrait également pouvoir comprendre comment se réalise la conversion d'affectation précédente entre la classe de base et la classe dérivée. En fait, il s'agit de retirer la partie de la classe mère dans la sous-classe pour la construction de copie pour créer une nouvelle parent, car la sous-classe doit contenir des variables membres de la classe parent.

 

Sixième, Cheng Cheng et You Yuan

Les relations d'amis ne peuvent pas être héritées , c'est-à-dire que les amis de la classe de base ne peuvent pas accéder aux membres privés et protégés de la sous-classe

class student;
class person
{
public:
	friend void Test(const person& p, const student& s);
protected:
	string _name = "张三";
	int _age = 18;
};

class student :public person
{

protected:
	int _stuNumber=1111111;
};

void Test(const person& p,const student& s)
{
	cout << p._name << endl;
	cout << p._age << endl;
	cout << s._stuNumber << endl;
}
int main()
{
	person pp;
	student aa;

	Test(pp,aa);
	
	return 0;
}

 Six, héritage et membres statiques

Si la classe de base définit un membre statique statique , il n'y a qu'un seul membre de ce type dans tout le système d'héritage . Quel que soit le nombre de sous-classes dérivées, il n'y a qu'une seule instance de membre statique

Résumer

Ce qui précède est tout le contenu de cet article. Il explique principalement la définition, le format, la conversion des affectations des classes dérivées et des classes de base, la portée de l'héritage et les six constructeurs par défaut des sous-classes. Les amis ne peuvent pas hériter. Et l'héritage des membres statiques. J'espère que les gars de fer peuvent gagner quelque chose.

Dans le prochain article, nous allons parler du faux héritage dans l'héritage, et espérons que les vétérans continueront à le soutenir.

Je suppose que tu aimes

Origine blog.csdn.net/zcxmjw/article/details/129865639
conseillé
Classement