Android C ++シリーズ:C++のベストプラクティス4多重継承と仮想継承

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して14日目です。クリックしてイベントの詳細をご覧ください

1.背景

JavaとC++を構文レベルで比較する場合、C ++の多重継承について言及する必要があります。Androidは単一継承であり、C++は多重継承であることがわかっています。大規模なプロジェクトでは、必然的に多重継承が使用されます。この記事では、C++の多重継承のいくつかの機能を分析します。

2.多重継承を実装するにはどうすればよいですか?

C ++では、派生リストに複数の基本クラスを含めることができます。

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

多重継承に関するいくつかの注意:

  1. 各基本クラスには、オプションのアクセス指定子が含まれています。
  2. 派生クラスリストには、すでに定義されているクラスのみを含めることができます。これらのクラスは最終的なものにすることはできません。
  3. C ++には、派生クラスが継承できる継承の累積数に関する特別な規定はありませんが、同じ基本クラスは派生クラスリストに1回しか表示できません。例:はい。Sub:public Base1,public Base2,public Base3 ....ただし、そうではありませんSub:public Base1,Base1

3.多重継承で各基本クラスから継承された状態

多重継承では、サブクラスオブジェクトには各基本クラスのサブオブジェクトが含まれます。たとえば、SubはBase1、Base2を継承し、Base1はBaseから継承します。Subオブジェクトの構造は次のとおりです。

image-20220416220303128.png

派生クラスのオブジェクトを作成すると、そのすべての基本クラスサブオブジェクトが同時に作成および初期化され、複数継承された派生クラスのコンストラクター値は、その直接のサブクラスのみを初期化できます。

サブクラスのコンストラクタ初期化子リストは、各直接の親クラスに個別に引数を渡します。親クラスの構築順序は、派生クラスコンストラクタ初期化子リストの基本クラスの順序に関係なく、派生リストに表示される基本クラスの順序と一致しています。この文を理解する方法は?つまり、建設オーダーはclass Sub : public Base1, public Base2このオーダーに関連していますがSub::Sub(std::string name):Base1(name),Base2(){}、関連していません。

新しいC++11標準では、派生クラスはその基本クラスの1つまたは複数からコンストラクターを継承できますが、同じコンストラクターが複数の基本クラスから統合されている場合、プログラムは失敗します。

たとえば、Base1とBase2const st::string&にパラメーターを持つコンストラクターがある場合、SubはBase1を同時に継承し、Base2はエラーになります。このとき、Subの独自のコンストラクターを定義する必要がありますSub:Sub(const std::string &s):Base1(s),Base2(s)

4.多重継承での型変換

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

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{

}
复制代码

この場合、Baseは実際には2回継承され、デフォルトでは、派生クラスには継承チェーンの各クラスに対応するサブセクションが含まれています。派生プロセス中にクラスが複数回出現する場合、派生クラスには基本クラスの複数のサブオブジェクトが含まれます。問題?一言で言えば、資源の浪費。

そして、仮想継承はこの問題を解決します。その目的は、クラスがその基本クラスを共有する意思があることを宣言することです。共有基本クラスサブオブジェクトを仮想基本クラスと呼びます。このように、仮想基本クラスが継承システムに何度表示されても、派生クラスには共有仮想基本クラスサブオブジェクトが1つだけあります。

**ベストプラクティス:**実際のシナリオでは、中間レベルの基本クラスは、一般に負の問題がない仮想継承としてその継承を宣言します。このようにして、後続のユーザーまたは拡張機能に便利さを提供できます。

7.まとめ

このテキストでは、C ++の多重継承と複数のインターフェイスを実装するJavaの違いを紹介し、多重継承と型変換、リソースルックアップ、および多重継承での仮想継承を紹介しています。

おすすめ

転載: juejin.im/post/7087213179956101134