Série Android C++ : Bonnes pratiques C++ 4 Héritage multiple et héritage virtuel

Prenez l'habitude d'écrire ensemble ! C'est le 14ème jour de ma participation au "Nuggets Daily New Plan·April Update Challenge", cliquez pour voir les détails de l'événement .

1. Origines

Lorsque nous comparons Java et C++ au niveau de la syntaxe, nous devons mentionner l'héritage multiple de C++.Nous savons qu'Android est un héritage unique et que C++ est un héritage multiple. L'héritage multiple sera inévitablement utilisé dans les grands projets.Cet article analyse certaines fonctionnalités de l'héritage multiple C++.

2. Comment implémenter l'héritage multiple ?

En C++, nous pouvons inclure plusieurs classes de base dans la liste dérivée :

class Sub : public Base{
	...
}
class SubA : public Base1, public Base2{
	...
}
复制代码

Quelques notes sur l'héritage multiple :

  1. Chaque classe de base contient un spécificateur d'accès facultatif ;
  2. La liste des classes dérivées ne peut contenir que des classes qui ont déjà été définies et ces classes ne peuvent pas être finales ;
  3. C++ n'a pas de dispositions spéciales sur le nombre cumulé d'héritages dont les classes dérivées peuvent hériter, mais la même classe de base ne peut apparaître qu'une seule fois dans la liste des classes dérivées. Par exemple : Oui Sub:public Base1,public Base2,public Base3 ...., mais pas Sub:public Base1,Base1.

3. État hérité de chaque classe de base en héritage multiple

Dans l'héritage multiple, l'objet sous-classe contient des sous-objets de chaque classe de base. Par exemple, Sub hérite de Base1, Base2 et Base1 hérite de Base. La structure de l'objet Sub est la suivante :

image-20220416220303128.png

La construction d'un objet d'une classe dérivée construira et initialisera simultanément tous ses sous-objets de classe de base, et la valeur du constructeur d'une classe dérivée à héritage multiple ne peut initialiser que ses sous-classes directes.

La liste d'initialisation du constructeur d'une sous-classe transmet des arguments à chaque classe parent immédiate individuellement. L'ordre de construction de la classe parent est cohérent avec l'ordre dans lequel les classes de base apparaissent dans la liste dérivée, quel que soit l'ordre des classes de base dans la liste d'initialisation du constructeur de classe dérivée. Comment comprendre cette phrase ? C'est-à-dire que l'ordre de construction est lié à class Sub : public Base1, public Base2cet ordre, mais Sub::Sub(std::string name):Base1(name),Base2(){}pas lié à.

Dans le nouveau standard C++11, une classe dérivée est autorisée à hériter des constructeurs d'une ou plusieurs de ses classes de base, mais si le même constructeur est intégré à partir de plusieurs classes de base, le programme échouera.

Par exemple, Base1 et Base2 ont des const st::string&constructeurs avec des paramètres, puis Sub hérite de Base1 en même temps, et Base2 fera une erreur. À ce stade, nous devons définir notre propre constructeur pour Sub Sub:Sub(const std::string &s):Base1(s),Base2(s).

4. Conversion de type en héritage multiple

子类继承多个父类的情况下,我们可以令某个可访问基类的指针或引用直接指向一个子类对象。比如:

Base1 *base1 = new Sub();
Base2 *base2 = new Sub();
Base base = new Sub();
复制代码

编译器不会在子类像基类的几种转换中进行比较和选择,因为它认为转换成哪个父类都行。但是这样会带来二义性,比如重载方法时:

void action(const Base1&);
void action(const Base2&);
复制代码

当我们给action传Sub对象时就会出问题,因为编译器不知道怎么转换了。

所以我们在重载方法时要注意这种类型转换可能引起的问题。

5. 多重继承中的资源查找

在Java中我们查找属性时先从子类找,找不到再找父类,C++也是类似,但是在多重继承的结构中可能会有些复杂。

因为在查找过程中,会在所有直接基类中同时进行,如果名字在多个基类中都被找到,则这个属性的名字就产生了二义性。

我们思考一个问题,在Java中,如果我们定义了两个接口A,B,它们都有void test()这么一个方法,那么我们的Test类如果同时实现了A,B接口,具体的类该怎么实现呢?

在Java中确实比较简单,只需要实现一个test()方法就可以,但是在C++的多重继承中,父类不仅有方法还有属性,这种二义性该怎么解决呢?在C++中,对于一个派生类来说,从它的几个基类中分别继承名字相同的成员是完全合法的,只是需要在我们使用这些名字是加上前缀限定符明确指定它属于哪个基类(不调用不会出错,如果调用了还没有加前缀就会出错)。

比如上面说的test方法,我们的子类可以使用sub->Base1::text()方法来调用。

还有一种更复杂的情况,就是派生类继承的两个基类有函数名相同,但是参数列表不同的方法,这样查找是更容易出错。

**最佳实践:**为了避免二义性,除了我们在调用时加前缀,最好的办法是在派生类中为这个函数定义一个自己的版本,在函数内部来屏蔽这些二义性。比如:

int Sub::getMax() const
{
	return std::max(Base1::getMax(), Base2::getMax());
}
复制代码

6. 虚继承

我们在派生类列表中见过这么一种形式:

class Base1:public virtual Base{
...
}
复制代码

virtual代表虚继承,那么虚继承是做什么,要解决什么问题呢?

比如这样的一种结构:

class Base{
protected:
	int num;
}
class Base1:public Base{

}
class Base2:public Base{

}
class Sub:public Base1, Base2{

}
复制代码

Dans ce cas, Base est en fait hérité deux fois et, par défaut, la classe dérivée contient des sous-sections correspondant à chaque classe de la chaîne d'héritage. Si une classe apparaît plusieurs fois au cours du processus de dérivation, la classe dérivée contiendra plusieurs sous-objets de la classe de base. Le problème? En un mot, un gaspillage de ressources.

Et l'héritage virtuel résout ce problème, son but est de faire déclarer à une classe qu'elle est prête à partager sa classe de base. Nous nous référons aux sous-objets de la classe de base partagée en tant que classes de base virtuelles. Ainsi, quel que soit le nombre d'apparitions de la classe de base virtuelle dans le système d'héritage, il n'y a qu'un seul sous-objet de classe de base virtuelle partagé dans la classe dérivée.

**Meilleure pratique :** Dans des scénarios pratiques, la classe de base au niveau intermédiaire déclare son héritage en tant qu'héritage virtuel généralement sans problèmes négatifs. De cette façon, nous pouvons offrir une commodité aux utilisateurs ou extensions ultérieurs.

7. Résumé

Le texte présente la différence entre l'héritage multiple C++ et Java implémentant plusieurs interfaces, et introduit l'héritage multiple et la conversion de type, la recherche de ressources et l'héritage virtuel dans l'héritage multiple.

Je suppose que tu aimes

Origine juejin.im/post/7087213179956101134
conseillé
Classement