19

単一の継承は実装が簡単です。例えば、Cで、継承はとしてシミュレートすることができます。C++の多重継承はどのように実装されていますか?

struct Base { int a; } 
struct Descendant { Base parent; int b; } 

しかし、多重継承では、コンパイラは新たに構築されたクラス内の複数の親を手配しなければなりません。どうしたの?

私が見ている問題は、親がABかBAで整理されているのか、それとも別の方法で整理されているのでしょうか?そして、私がキャストを行う場合:

SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents; 

コンパイラは元のポインタを変更するかどうかを検討する必要があります。同様のトリッキーなことがバーチャルで必要です。

+0

http://en.wikipedia.org/wiki/Diamond_problem – Dario

+0

Cのシミュレーションでは、VTable(実装の詳細)ポインタが忘れています。 –

+1

@Dario:この記事では、多重継承のオーバーロード問題について説明しますが、C++でのオブジェクトレイアウトとオブジェクトのキャストについては何も含まれていません。 – mmmmmmmm

答えて

10

C++の生みの親から以下の論文は、多重継承の可能な実装について説明します。

Multiple Inheritance for C++ - ビャーネ・ストロヴストルップを

+3

代替リンク:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.23.4735&rep=rep1&type=pdf –

+0

あなたが何をしているかによって、両方を読んでください。これ(_Stroustrup_のもの)は非常に詳細であり、理論をカバーしています。下の_Nemanja_(_MSDN_のもの)によって提案されたものは、より簡単で実際の実装をカバーしています。 –

+0

どちらのリンクも今は死んでいますが、https://www.usenix.org/publications/compsystems/1989/fall_stroustrup.pdfが私のために働いています。 –

5

VC++での実装方法はthis pretty old MSDN articleでした。

+1

このリンクは質問に答えるかもしれませんが、答えの本質的な部分をここに含めて参考にしてください。リンクされたページが変更された場合、リンクのみの回答は無効になります。 - [レビューから](/レビュー/低品質の投稿/ 18125623) – CDspace

0

それは完全にどのようにコンパイルされているのですか?しかし、私は一般的にvtablesのheirarchical構造を通してそれを信じています。

1

両親は、彼らが指定していることを順に配置されている:

class Derived : A, B {} // A comes first, then B 

class Derived : B, A {} // B comes first, then A 

2番目のケースは、コンパイラ固有の方法で処理されます。一般的な方法の1つは、余分なデータを格納するために、プラットフォームのポインタサイズよりも大きなポインタを使用することです。

+0

これはおそらく一般的ですが、私はそれが必要ではないと思います。 –

+0

私はそれを他の方法で見てきました。それはすべて実装に依存します。 –

+0

オーダは、コンストラクタ呼び出しの順序にのみ影響します。しかし、レイアウトは不特定です。私はしばらく前にテストを行い、GCCは空の基底クラスの最適化を利用するために空の基底を最初にメモリに置きます。 –

1

これは実際にはC++固有の興味深い問題です。複数のディスパッチや複数の継承(CLOSなど)を持つ言語を使用している場合も、より複雑になります。

人々はすでに、問題にアプローチするさまざまな方法があることに気付きました。

SecondBase base = (SecondBase *) object_with_base1_and_base2_parents; 

は、コンパイラが変更するかどうかを検討する必要があります:あなたは、私はキャストを行う場合は、...この文脈で興味深いメタオブジェクトプロトコル(のMOP)について少し読ん

+1

言語がrefernceオブジェクトへの "生の"ポインタをサポートしておらず、PODの下位互換性をサポートしていないのであれば実装がずっと簡単だと思います。なぜなら、オブジェクトへのポインタのキャストは非常に難しいからです。言語は、参照クラスとクラスインスタンスに必要なメタ情報を追加できます。 C++では非常に簡単にはできませんでした。 – mmmmmmmm

5

とを見つけるかもしれませんまたは元のポインタではありません。仮想と同様のやりにくいこと。非Virutalの継承と

これはあなたが思っているより少ないトリッキーです - キャストがコンパイルされる時点では、コンパイラは、派生クラスの正確なレイアウトを知っている(すべての後に、コンパイラは、レイアウトをしました)。通常、起きるのは固定オフセット(基底クラスの1つに対してゼロかもしれません)が派生クラスポインタから加算/減算されることだけです。

virutal継承では、もう少し複雑かもしれません.vtbl(または同様のもの)からのオフセットを取得する必要があります。

Stan Lippmanの書籍"Inside the C++ Object Model"には、このようなことがどのように作用するのか(しばしば実際に作用するのか)の非常に良い説明があります。

0

私は簡単な実験を行っています

class BaseA { int a; }; 
class BaseB { int b; }; 
class Descendant : public BaseA, BaseB {}; 
int main() { 
     Descendant d; 
     BaseB * b = (BaseB*) &d; 
     Descendant *d2 = (Descendant *) b; 
     printf("Descendant: %p, casted BaseB: %p, casted back Descendant: %p\n", &d, b, d2); 
} 

出力は次のとおりです。

Descendant: 0xbfc0e3e0, casted BaseB: 0xbfc0e3e4, casted back Descendant: 0xbfc0e3e0 

それは静的なキャストは、常に「コンテンツに触れることなくタイプを変更する」という意味ではありませんことを認識することが良いことです。 (まあ、データ型が互いに適合しない場合、コンテンツへの干渉もありますが、それは違う状況です)。

関連する問題