多重継承とは? わかりやすく解説

Weblio 辞書 > コンピュータ > IT用語辞典 > 多重継承の意味・解説 

多重継承

読み方たじゅうけいしょう
【英】multiple inheritance

多重継承とは、複数親クラス継承するというオブジェクト指向プログラミングテクニックのことである。

多重継承は、あるクラス複数親クラス継承することで、複数親クラス特徴引き継ぐことである。C++などで可能な方法であり比較的よく利用されている。

多重継承では、機能組み合わせるという意味では有効であるが、メソッド名の衝突問題継承関係が複雑になり、クラス分割再利用が困難になることで継承本質から逸脱するという大きな問題孕んでいる。

C++では、この問題に対して仮想継承仕組み適用して回避している。また、多重継承が許されていないJavaでは、インターフェース継承外延継承)を使うことで多重継承に似たテクニック適用できる

プログラミングのほかの用語一覧
オブジェクト指向:  疎結合  スーパークラス  COM  多重継承  単一継承  デストラクタ  UML

継承 (プログラミング)

(多重継承 から転送)

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2023/10/17 00:49 UTC 版)

コンピュータプログラミングにおける継承(けいしょう、: inheritance)とは、任意のオブジェクトの特性を、他のオブジェクトの特性の基礎にするためのメカニズムと定義されている。

基礎にされる継承元は親、その継承先は子と呼ばれて、状態機能定数注釈などが引き継がれるが、コンストラクタデストラクタは対象外になる。その親と子の関係を、クラスベースOOPはスーパークラスサブクラスの関係で、プロトタイプベースOOPはプロトタイプとクローンの関係で導入している[1]

概要

継承図

継承は、他のオブジェクトの特性(データ手続き関数定数アノテーションなど)を引き継ぐという概念であり、引き継いだオブジェクトがどのような性質を持ち、どのように振る舞うのかは全くの任意になる。引き継ぎかたは、リクエストされた特性をそのオブジェクトが持たない場合は、自動的に上位オブジェクトの方でサーチするという方式が一般的であり、これは暗黙の委譲(delegation)ベースとも呼ばれる。他には、インスタンス化時にその型の継承チェーンを走査してその全要素を集めて同名重複要素を解決して1つの実体を生成するという方式もあり、これは連結(concatenation)ベースとも呼ばれる。

他オブジェクトの特性を引き継ぐという概念は、それに新しい特性群を付け足しての手軽なオブジェクトの機能拡張と、引き継がれる共通の特性群を上位ノードにしたオブジェクトの分類体系化をもたらしている。これは差分プログラミングとも呼ばれ、プログラムの再利用性と保守性を高めるとされている。

継承(inheritance)とサブタイピング(subtyping)は混同されやすい。ここでのサブタイピングは、親オブジェクトに対する子オブジェクトの安全な代替/代入(substitute)を保証する継承という意味で使われている。それに対してのただの継承は、親オブジェクトの特性をただ引き継ぐことに専念しており、安全な代替/代入には無関心である。たとえ話としては、親の白黒映画をカラー映画化するのが代替可能なサブタイピングであり、親の白黒映画をメディアミックス的グッズ販売につなげるのが代替不可な継承になる。継承に代入可能性(substitutability)を順守させてサブタイピングにすることを提唱しているのが、リスコフの置換原則である。

継承と対比される概念にコンポジション (合成)英語版がある。継承のサブタイピング用法の上位概念と下位概念Is-a)に対して、合成は(Has-a)であるが、継承の非サブタイピング用法では、スーパークラスとサブクラスの関係が(Is-aでもHas-aでもない)の関係になることがしばしばあるので、それと合成との使い分けが重視されるようになっている。

継承の目的

差分プログラミング

差分プログラミング(difference coding)とは、クラス間の共通構成を、各クラスの特有構成に引き継がせるようにして、重複構成の削減と、分類体系化をもたらすことを目的にした継承の用法である。これは、クラスに新機能を付け足しての手軽なクラス拡張目的と、クラスの共通部分を括りだして体系化するクラス分類目的の双方に使われた。

差分プログラミングは、継承の元々の用法であり、プログラムの再利用性と保守性を高めると見なされていたが、後年になると階層分散配置されたデータとメソッドの把握のしづらさによる弊害の方が目立つようになって、この用法を否定する傾向が強くなった。同時にその代替としての合成英語版が重視されるようになっている。

サブタイピング

サブタイピング(subtyping)とは、スーパークラスのインスタンスを、サブクラスのインスタンスで安全に代替できることを指針にした継承の用法である。基底クラスの変数への、派生クラスのインスタンスの安全な代入可能性(substitutability)を保証している。これはIs-a関係とも言われる。サブタイピングでは、派生側でのフィールドの追加は抑制され、基底側からのメソッド実装の引き継ぎも抑制されており、基底側からのメソッド定義(メソッドシグネチャ)の引き継ぎが重視されている。派生インスタンスが代入された基底変数のメソッド名から派生メソッド内容が呼び出される言語機能は、メソッドオーバーライドと呼ばれ、その機能概念は動的ディスパッチ英語版と呼ばれる。サブタイピングは動的ディスパッチに焦点を当てた継承と解釈できる。

具象メソッド(定義+実装)の引き継ぎは実装継承(implementation inheritance)またはコード継承(code inheritance)と呼ばれており、抽象メソッド(定義だけ)の引き継ぎは界面継承(interface inheritance)と呼ばれている。

Is-a関係サブタイピング主体の継承関係は、UMLクラス図では汎化/特化の関係に投影されている。抽象メソッドだけで構成される純粋抽象クラスは、インターフェースと呼ばれており、それとの継承関係はUMLクラス図では実現/実装の関係に投影されている。

サブタイピングのコーディング例はこうなる。

#include <iostream>
#include <string>
#include <typeinfo>

class Base {
public:
    virtual ~Base() {}
    virtual std::string greet() const = 0;
};

class Derived : public Base {
    virtual ~Derived() { std::cout << "Destructor of Derived is called." << std::endl; }
    virtual std::string greet() const { return "Hello!"; }
};

int main() {
    Base* b = new Derived(); // OK
    std::cout << "Message: " << b->greet() << std::endl;
    std::cout << "Is instance of Derived? " << std::boolalpha << (typeid(*b) == typeid(Derived)) << std::endl;
    delete b;
    return 0;
}

多重継承

クラスに複数のスーパークラスを持たせることを多重継承という。単一継承と異なり、多重継承では、スーパークラス上のメンバサーチが複数方向に分かれるので、どのメンバが参照されるのかの把握が困難になるという欠点がある。特にフィールドの多重継承・分散配置は、早期に原則禁止が一般化している。メソッドの方はやむなく許容されたので、メソッド決定順序(MRO)問題が取り沙汰された。MRO問題を解決するために導入されたのが、インターフェースの実装やトレイトのインクルードであり、双方はデータ主体クラスを単一継承にしてメソッド主体クラスを多重継承にするというハイブリッド継承の担い手になった。

また、多重継承上のスーパークラスの重複による菱形継承問題も問題視されるようになっている。菱形継承問題の解決策としては、C++/Eiffel発の仮想継承Eiffel発のリネーミング、Python発のC3線形化などがある。

多重継承と仮想継承のコーディング例を以下に示す。同一のクラスから継承している複数の派生クラスを多重継承して1つのクラスを作る場合に始めの基底クラスの存在をどうするかによって仮想継承と通常の多重継承の2つに分かれる。

class Base {
public:
    int n;
};

// 非仮想継承。
class DerivedNV1 : public Base { /* ... */ };
class DerivedNV2 : public Base { /* ... */ };

// 仮想継承。
class DerivedV1 : public virtual Base { /* ... */ };
class DerivedV2 : public virtual Base { /* ... */ };

class DerivedNV : public DerivedNV1, public DerivedNV2 { /* ... */ };
class DerivedV : public DerivedV1, public DerivedV2 { /* ... */ };

int main() {
    DerivedNV nv;
    //nv.n = 0; // 曖昧さが解決できないためコンパイルエラー。
    nv.DerivedNV1::n = 0;
    nv.DerivedNV2::n = 0;
    DerivedV v;
    v.n = 0; // コンパイルエラーにはならない。
    return 0;
}

この例のような状態は特に菱形継承(ダイアモンド継承)と呼ばれる。

仮想継承でない場合、DerivedNVインスタンスにはDerivedNV1の基底のBase::nDerivedNV2の基底のBase::nという2つのnが別に存在することになる(メンバ関数も同様)。一方、仮想継承した場合、DerivedVのインスタンスにはBaseの部分はただ1つしか存在しない。DerivedV1の基底とDerivedV2の基底が共有されている状態である。

先発OOP言語のC++Eiffelでは実装の多重継承ができたが、後発言語のJavaC#では実装は単一継承限定にされ、代わりにインターフェースの多重継承(界面の多重継承)が導入されている。なぜなら実装の多重継承はメリットよりもデメリットのほうが多いとみなされたためである。

  1. 継承関係が複雑になるため全体の把握が困難になる。
  2. 名前の衝突。同じ名前を複数の基底クラスがそれぞれ別の意味で用いていた場合、その両方を派生クラスでオーバーライドするのが困難。
  3. 処理系の実装が複雑になってしまう。
  4. 仮想継承にしていない場合に同一の基底クラスが複数存在してしまう(これが望ましい場面もあるが)。これの何が問題かというと、最初は仮想継承していなかったものを、後から仮想継承にしたくなったときに、変更点を洗い出すのが大変になるからである。つまり仮想継承を使用するには設計をきちんと行う必要があるということである。

しかしながら多重継承を使う方が直感的になる場合もあるとの主張もあり、どちらが正しいとは言えない状況である。

カプセル化の可視性と継承の可視性

カプセル化の可視性(public/protected/package/private)によって、各スーパークラスメンバの受け継ぎが取捨選択されることは、派生型に対する継承の大きな特徴である。privateメンバはサブクラスに受け継がれない。packageメンバは外部パッケージのサブクラスには受け継がれない。

継承の可視性は、スーパークラスメンバ(フィールド/メソッド)の可視性に更に制約をかける機能である。三段階ある。

  1. public継承 - そのままの継承。
  2. protected継承 - スーパークラスのpublicメンバを、protectedメンバに引き下げて継承する。
  3. private継承 - スーパークラスのpublic/protectedメンバを、privateメンバに引き下げて継承する。

これはC++で導入されていたが、後継OOP言語ではほとんど採用されていない。

ミックスイン

ミックスイン(Mix-in)は、多重継承問題の解決策を発端にしたもう1つの継承方法論である。メソッドの集合体を継承することで、その機能をクラスに注入することを目的にしており、メソッド集合体とクラスの間には汎化/特化の関係がないままで、多重継承を前提にしている。そのメソッド集合体はトレイトとされることが多く、他にモジュール、プロトコル、ロールといった形態もある。トレイトの継承はインクルードと呼ぶのが好まれ多重継承前提である。ミックスインはそれらをひっくるめた方法論としての用語になっている。

界面継承のインターフェース(抽象メソッドをまとめたクラス)と、Mix-in継承のトレイト(独立メソッドをまとめたモジュール)は双方とも多重継承前提なのでよく対比されて説明される。双方の違いを列挙すると以下のようになる。

  • 界面継承は抽象メソッドをクラスに相続させるのに対して、Mix-in継承は独立メソッドをクラスに贈与する。
  • 界面継承はインターフェースの継承先クラスに実装メソッドを記述するが、Mix-in継承はトレイトに実装メソッドを記述する。
  • 界面継承は同名アルゴリズムを個々のクラスのメソッドに分散記述するが、Mix-in継承は1つのメソッドに個々のクラスのアルゴリズムを一括記述する。
  • インターフェースはデータメンバを持つことを想定されていないが、トレイトはデータメンバを持つ。すなわち界面継承は派生メソッドたち専用の共有データを持てないが、Mix-in継承はそれが可能である。
  • 界面継承はthis参照を暗黙使用できるが、Mix-in継承は不可なのでThis参照の明示的な引数渡しや関連型の機能が必要になる。
  • インターフェースは記名的型付けであるのに対して、トレイトは構造的型付けで識別されることが多い。

UMLにおける継承

統一モデリング言語 (UML) のクラス図では、サブクラスから見たスーパークラスは汎化 (generalization) 、スーパークラスから見たサブクラスは特化 (specialization) と呼ばれる。

純粋抽象クラスはインターフェースと定義されており、クラスから見たインターフェースは実現(realization)[要検証]、クラスがインターフェースを継承することは実装(implementation)と呼ばれる。

サブタイピング用法の投影は汎化/特化の関係であり、インターフェース用法の投影は実現/実装の関係である。ミックスイン用法はUMLクラス図で扱われていない関係であり、差分プログラミング用法も同様である。

脚注

  1. ^ MDN contributors (2022年9月17日). "継承とプロトタイプチェーン - JavaScript". developer.mozilla.org. 2022年9月18日閲覧

関連項目


多重継承

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2021/12/05 08:46 UTC 版)

継承 (プログラミング)」の記事における「多重継承」の解説

クラス複数スーパークラス持たせることを多重継承という。単一継承異なり、多重継承では、スーパークラス上のメンバサーチが複数方向分かれるので、どのメンバ参照されるのかの把握困難になるという欠点がある。特にフィールドの多重継承・分散配置は、早期原則禁止一般化している。メソッドの方はやむなく許容されたので、メソッド決定順序MRO問題取り沙汰された。MRO問題解決するために導入されたのが、インターフェース実装トレイトインクルードであり、双方データ主体クラス単一継承にしてメソッド主体クラスを多重継承にするというハイブリッド継承担い手になったまた、多重継承上のスーパークラス重複による菱形継承問題問題視されるようになっている菱形継承問題解決策としては、C++/Eiffel発の仮想継承Eiffel発のリネーミング、Python発のC3線形化などがある。 多重継承と仮想継承コーディング例を以下に示す。同一クラスから継承している複数派生クラスを多重継承して1つのクラス作る場合始め基底クラス存在どうするかによって仮想継承通常の多重継承の2つ分かれるclass Base {public: int n;};// 非仮想継承class DerivedNV1 : public Base { /* ... */ };class DerivedNV2 : public Base { /* ... */ };// 仮想継承class DerivedV1 : public virtual Base { /* ... */ };class DerivedV2 : public virtual Base { /* ... */ };class DerivedNV : public DerivedNV1, public DerivedNV2 { /* ... */ };class DerivedV : public DerivedV1, public DerivedV2 { /* ... */ };int main() { DerivedNV nv; //nv.n = 0; // 曖昧さ解決できないためコンパイルエラー。 nv.DerivedNV1::n = 0; nv.DerivedNV2::n = 0; DerivedV v; v.n = 0; // コンパイルエラーにはならないreturn 0;} この例のような状態は特に菱形継承ダイアモンド継承)と呼ばれる仮想継承ない場合、DerivedNVのインスタンスにはDerivedNV1の基底Base::nとDerivedNV2の基底Base::nという2つのnが別に存在することになる(メンバ関数も同様)一方仮想継承した場合、DerivedVのインスタンスにはBase部分はただ1つしか存在しない。DerivedV1の基底とDerivedV2の基底共有されている状態である。 先発OOP言語C++Eiffelでは実装の多重継承ができたが、後発言語JavaC#では実装単一継承限定にされ、代わりにインターフェースの多重継承(界面の多重継承)が導入されている。なぜなら実装の多重継承はメリットよりもデメリットのほうが多いとみなされたためである。 継承関係が複雑になるため全体把握困難になる。 名前の衝突。同じ名前を複数基底クラスそれぞれ別の意味用いていた場合、その両方派生クラスオーバーライドするのが困難。 処理系実装複雑になってしまう。 仮想継承にしていない場合同一基底クラス複数存在してしまう(これが望ましい場面もあるが)。これの何が問題というと最初仮想継承していなかったものを、後から仮想継承にしたくなったときに、変更点洗い出すのが大変になるからである。つまり仮想継承使用するには設計をきちんと行う必要があるということである。 しかしながら多重継承を使う方が直感的になる場合もあるとの主張もあり、どちらが正しとは言えない状況である。

※この「多重継承」の解説は、「継承 (プログラミング)」の解説の一部です。
「多重継承」を含む「継承 (プログラミング)」の記事については、「継承 (プログラミング)」の概要を参照ください。

ウィキペディア小見出し辞書の「多重継承」の項目はプログラムで機械的に意味や本文を生成しているため、不適切な項目が含まれていることもあります。ご了承くださいませ。 お問い合わせ


英和和英テキスト翻訳>> Weblio翻訳
英語⇒日本語日本語⇒英語
  

辞書ショートカット

すべての辞書の索引

「多重継承」の関連用語

多重継承のお隣キーワード
検索ランキング

   

英語⇒日本語
日本語⇒英語
   



多重継承のページの著作権
Weblio 辞書 情報提供元は 参加元一覧 にて確認できます。

   
IT用語辞典バイナリIT用語辞典バイナリ
Copyright © 2005-2025 Weblio 辞書 IT用語辞典バイナリさくいん。 この記事は、IT用語辞典バイナリの【多重継承】の記事を利用しております。
ウィキペディアウィキペディア
All text is available under the terms of the GNU Free Documentation License.
この記事は、ウィキペディアの継承 (プログラミング) (改訂履歴)の記事を複製、再配布したものにあたり、GNU Free Documentation Licenseというライセンスの下で提供されています。 Weblio辞書に掲載されているウィキペディアの記事も、全てGNU Free Documentation Licenseの元に提供されております。
ウィキペディアウィキペディア
Text is available under GNU Free Documentation License (GFDL).
Weblio辞書に掲載されている「ウィキペディア小見出し辞書」の記事は、Wikipediaの継承 (プログラミング) (改訂履歴)の記事を複製、再配布したものにあたり、GNU Free Documentation Licenseというライセンスの下で提供されています。

©2025 GRAS Group, Inc.RSS